Compare commits
2 commits
532d37ce81
...
b6db17e7d8
Author | SHA1 | Date | |
---|---|---|---|
b6db17e7d8 | |||
e91bf96e74 |
12 changed files with 140 additions and 44 deletions
3
frontend/.gitignore
vendored
3
frontend/.gitignore
vendored
|
@ -20,3 +20,6 @@ pnpm-debug.log*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
# Generated source files
|
||||||
|
src/generated/
|
|
@ -8,6 +8,7 @@
|
||||||
"build": "run-p type-check \"build-only {@}\" --",
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
|
"update-types": "openapi-typescript http://localhost:8080/v3/api-docs.yaml -o src/generated/api-spec.ts",
|
||||||
"type-check": "vue-tsc --build --force",
|
"type-check": "vue-tsc --build --force",
|
||||||
"lint": "eslint . --fix"
|
"lint": "eslint . --fix"
|
||||||
},
|
},
|
||||||
|
@ -17,8 +18,7 @@
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vuetify": "^3.8.1",
|
"vuetify": "^3.8.1",
|
||||||
"openapi-fetch": "^0.14.0",
|
"openapi-fetch": "^0.14.0"
|
||||||
"openapi-react-query": "^0.5.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.24.0",
|
"@eslint/js": "^9.24.0",
|
||||||
|
|
36
frontend/pnpm-lock.yaml
generated
36
frontend/pnpm-lock.yaml
generated
|
@ -17,9 +17,6 @@ importers:
|
||||||
openapi-fetch:
|
openapi-fetch:
|
||||||
specifier: ^0.14.0
|
specifier: ^0.14.0
|
||||||
version: 0.14.0
|
version: 0.14.0
|
||||||
openapi-react-query:
|
|
||||||
specifier: ^0.5.0
|
|
||||||
version: 0.5.0(@tanstack/react-query@5.80.10(react@19.1.0))(openapi-fetch@0.14.0)
|
|
||||||
roboto-fontface:
|
roboto-fontface:
|
||||||
specifier: '*'
|
specifier: '*'
|
||||||
version: 0.10.0
|
version: 0.10.0
|
||||||
|
@ -627,14 +624,6 @@ packages:
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@tanstack/query-core@5.80.10':
|
|
||||||
resolution: {integrity: sha512-mUNQOtzxkjL6jLbyChZoSBP6A5gQDVRUiPvW+/zw/9ftOAz+H754zCj3D8PwnzPKyHzGkQ9JbH48ukhym9LK1Q==}
|
|
||||||
|
|
||||||
'@tanstack/react-query@5.80.10':
|
|
||||||
resolution: {integrity: sha512-6zM098J8sLy9oU60XAdzUlAH4wVzoMVsWUWiiE/Iz4fd67PplxeyL4sw/MPcVJJVhbwGGXCsHn9GrQt2mlAzig==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^18 || ^19
|
|
||||||
|
|
||||||
'@tsconfig/node22@22.0.1':
|
'@tsconfig/node22@22.0.1':
|
||||||
resolution: {integrity: sha512-VkgOa3n6jvs1p+r3DiwBqeEwGAwEvnVCg/hIjiANl5IEcqP3G0u5m8cBJspe1t9qjZRlZ7WFgqq5bJrGdgAKMg==}
|
resolution: {integrity: sha512-VkgOa3n6jvs1p+r3DiwBqeEwGAwEvnVCg/hIjiANl5IEcqP3G0u5m8cBJspe1t9qjZRlZ7WFgqq5bJrGdgAKMg==}
|
||||||
|
|
||||||
|
@ -1458,12 +1447,6 @@ packages:
|
||||||
openapi-fetch@0.14.0:
|
openapi-fetch@0.14.0:
|
||||||
resolution: {integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==}
|
resolution: {integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==}
|
||||||
|
|
||||||
openapi-react-query@0.5.0:
|
|
||||||
resolution: {integrity: sha512-VtyqiamsbWsdSWtXmj/fAR+m9nNxztsof6h8ZIsjRj8c8UR/x9AIwHwd60IqwgymmFwo7qfSJQ1ZzMJrtqjQVg==}
|
|
||||||
peerDependencies:
|
|
||||||
'@tanstack/react-query': ^5.25.0
|
|
||||||
openapi-fetch: ^0.14.0
|
|
||||||
|
|
||||||
openapi-typescript-helpers@0.0.15:
|
openapi-typescript-helpers@0.0.15:
|
||||||
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
|
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
|
||||||
|
|
||||||
|
@ -1583,10 +1566,6 @@ packages:
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
react@19.1.0:
|
|
||||||
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
|
|
||||||
read-package-json-fast@4.0.0:
|
read-package-json-fast@4.0.0:
|
||||||
resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
|
resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
|
||||||
engines: {node: ^18.17.0 || >=20.5.0}
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
|
@ -2606,13 +2585,6 @@ snapshots:
|
||||||
'@rollup/rollup-win32-x64-msvc@4.40.0':
|
'@rollup/rollup-win32-x64-msvc@4.40.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tanstack/query-core@5.80.10': {}
|
|
||||||
|
|
||||||
'@tanstack/react-query@5.80.10(react@19.1.0)':
|
|
||||||
dependencies:
|
|
||||||
'@tanstack/query-core': 5.80.10
|
|
||||||
react: 19.1.0
|
|
||||||
|
|
||||||
'@tsconfig/node22@22.0.1': {}
|
'@tsconfig/node22@22.0.1': {}
|
||||||
|
|
||||||
'@types/cookie@0.6.0': {}
|
'@types/cookie@0.6.0': {}
|
||||||
|
@ -3543,12 +3515,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
openapi-typescript-helpers: 0.0.15
|
openapi-typescript-helpers: 0.0.15
|
||||||
|
|
||||||
openapi-react-query@0.5.0(@tanstack/react-query@5.80.10(react@19.1.0))(openapi-fetch@0.14.0):
|
|
||||||
dependencies:
|
|
||||||
'@tanstack/react-query': 5.80.10(react@19.1.0)
|
|
||||||
openapi-fetch: 0.14.0
|
|
||||||
openapi-typescript-helpers: 0.0.15
|
|
||||||
|
|
||||||
openapi-typescript-helpers@0.0.15: {}
|
openapi-typescript-helpers@0.0.15: {}
|
||||||
|
|
||||||
openapi-typescript@7.8.0(typescript@5.8.3):
|
openapi-typescript@7.8.0(typescript@5.8.3):
|
||||||
|
@ -3668,8 +3634,6 @@ snapshots:
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
react@19.1.0: {}
|
|
||||||
|
|
||||||
read-package-json-fast@4.0.0:
|
read-package-json-fast@4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
json-parse-even-better-errors: 4.0.0
|
json-parse-even-better-errors: 4.0.0
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
title="Manage your certificates"
|
title="Manage your certificates"
|
||||||
variant="text"
|
variant="text"
|
||||||
to="/cert-request"
|
to="/certificates"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
34
frontend/src/pages/certificates/[fingerprint].vue
Normal file
34
frontend/src/pages/certificates/[fingerprint].vue
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { fetchClient, type Schemas } from "@/plugins/client.ts";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const certificate = ref<Schemas["Certificate"] | null>(null);
|
||||||
|
|
||||||
|
async function getCertificate() {
|
||||||
|
const fingerprint = route.params.fingerprint as string;
|
||||||
|
const response = await fetchClient.GET("/api/certificates/{fingerprint}", {
|
||||||
|
params: {
|
||||||
|
path: {
|
||||||
|
fingerprint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
certificate.value = response.data ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCertificate()
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<form v-if="certificate">
|
||||||
|
<h2>Details des Zertifikats: {{ certificate.fingerprint }}</h2>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</template>
|
49
frontend/src/pages/certificates/index.vue
Normal file
49
frontend/src/pages/certificates/index.vue
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { fetchClient, type Schemas } from "@/plugins/client.ts";
|
||||||
|
|
||||||
|
const certificates = ref<Schemas["Certificate"][]>([]);
|
||||||
|
|
||||||
|
async function getCertificates() {
|
||||||
|
const response = await fetchClient.GET("/api/certificates");
|
||||||
|
certificates.value = response.data?.content ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCertificates();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<h2>Currently known certificates</h2>
|
||||||
|
<section id="no-certs-found" v-if="certificates.length === 0">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" class="text-center">
|
||||||
|
<h3>There seems to be nothing in here.</h3>
|
||||||
|
<p>Why don't you start with one of the following options.</p>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-btn primary>Request new certificate</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-btn>Import an existing certificate</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</section>
|
||||||
|
<section aria-label="Bekannte Zertifikate">
|
||||||
|
<ul v-if="certificates.length > 0">
|
||||||
|
<li v-for="cert in certificates" :key="cert.fingerprint">
|
||||||
|
<p>{{ cert.fingerprint }}</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
11
frontend/src/pages/certificates/new.vue
Normal file
11
frontend/src/pages/certificates/new.vue
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>New certificate</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
18
frontend/src/plugins/client.ts
Normal file
18
frontend/src/plugins/client.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import createFetchClient from "openapi-fetch";
|
||||||
|
import type { components, paths } from "@/generated/api-spec.ts";
|
||||||
|
import type { App } from "vue";
|
||||||
|
|
||||||
|
export const fetchClient = createFetchClient<paths>({
|
||||||
|
baseUrl: "http://localhost:8080"
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Schemas = components["schemas"];
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
const $api = {
|
||||||
|
install: (app: App) => {
|
||||||
|
app.config.globalProperties.$api = fetchClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default $api;
|
|
@ -1,10 +1,12 @@
|
||||||
import vuetify from './vuetify'
|
import vuetify from "./vuetify";
|
||||||
import router from '../router'
|
import router from "../router";
|
||||||
|
import client from "./client";
|
||||||
|
|
||||||
import type { App } from 'vue'
|
import type { App } from "vue";
|
||||||
|
|
||||||
export function registerPlugins (app: App) {
|
export function registerPlugins(app: App) {
|
||||||
app
|
app
|
||||||
.use(vuetify)
|
.use(vuetify)
|
||||||
.use(router)
|
.use(router)
|
||||||
|
.use(client);
|
||||||
}
|
}
|
||||||
|
|
3
frontend/typed-router.d.ts
vendored
3
frontend/typed-router.d.ts
vendored
|
@ -20,5 +20,8 @@ declare module 'vue-router/auto-routes' {
|
||||||
export interface RouteNamedMap {
|
export interface RouteNamedMap {
|
||||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||||
'/cert-request': RouteRecordInfo<'/cert-request', '/cert-request', Record<never, never>, Record<never, never>>,
|
'/cert-request': RouteRecordInfo<'/cert-request', '/cert-request', Record<never, never>, Record<never, never>>,
|
||||||
|
'/certificates/': RouteRecordInfo<'/certificates/', '/certificates', Record<never, never>, Record<never, never>>,
|
||||||
|
'/certificates/[fingerprint]': RouteRecordInfo<'/certificates/[fingerprint]', '/certificates/:fingerprint', { fingerprint: ParamValue<true> }, { fingerprint: ParamValue<false> }>,
|
||||||
|
'/certificates/new': RouteRecordInfo<'/certificates/new', '/certificates/new', Record<never, never>, Record<never, never>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class SecurityConfiguration {
|
||||||
// Allow unauthenticated access to OpenAPI and swagger documentation.
|
// Allow unauthenticated access to OpenAPI and swagger documentation.
|
||||||
// This should be removed or at least configurable at some point, but for now, this is fine (tm)
|
// This should be removed or at least configurable at some point, but for now, this is fine (tm)
|
||||||
http.authorizeHttpRequests(auth -> auth
|
http.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
|
.requestMatchers("/v3/api-docs/**", "/v3/api-docs.yaml", "/swagger-ui/**", "/swagger-ui.html")
|
||||||
.permitAll());
|
.permitAll());
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,18 @@ public class CertificatesEndpoint {
|
||||||
return ResponseEntity.ok(DocumentedPage.of(certificates));
|
return ResponseEntity.ok(DocumentedPage.of(certificates));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/certificates/{fingerprint}")
|
||||||
|
@Operation(
|
||||||
|
description = "Fetches a single certificate by the provided fingerprint",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public ResponseEntity<Certificate> getCertificate(@PathVariable String fingerprint) {
|
||||||
|
var certificate = certificateRepository.findByFingerprintIs(fingerprint);
|
||||||
|
return ResponseEntity.ok(certificate);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/certificates")
|
@PostMapping("/certificates")
|
||||||
@Operation(description = "Requests a new certificate", responses = {
|
@Operation(description = "Requests a new certificate", responses = {
|
||||||
@ApiResponse(responseCode = "400", description = "One of the provided parameters is invalid."),
|
@ApiResponse(responseCode = "400", description = "One of the provided parameters is invalid."),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue