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
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Generated source files
|
||||
src/generated/
|
|
@ -8,6 +8,7 @@
|
|||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"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",
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
|
@ -17,8 +18,7 @@
|
|||
"roboto-fontface": "*",
|
||||
"vue": "^3.5.13",
|
||||
"vuetify": "^3.8.1",
|
||||
"openapi-fetch": "^0.14.0",
|
||||
"openapi-react-query": "^0.5.0"
|
||||
"openapi-fetch": "^0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.24.0",
|
||||
|
|
36
frontend/pnpm-lock.yaml
generated
36
frontend/pnpm-lock.yaml
generated
|
@ -17,9 +17,6 @@ importers:
|
|||
openapi-fetch:
|
||||
specifier: ^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:
|
||||
specifier: '*'
|
||||
version: 0.10.0
|
||||
|
@ -627,14 +624,6 @@ packages:
|
|||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-VkgOa3n6jvs1p+r3DiwBqeEwGAwEvnVCg/hIjiANl5IEcqP3G0u5m8cBJspe1t9qjZRlZ7WFgqq5bJrGdgAKMg==}
|
||||
|
||||
|
@ -1458,12 +1447,6 @@ packages:
|
|||
openapi-fetch@0.14.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
|
||||
|
||||
|
@ -1583,10 +1566,6 @@ packages:
|
|||
queue-microtask@1.2.3:
|
||||
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:
|
||||
resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
@ -2606,13 +2585,6 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.40.0':
|
||||
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': {}
|
||||
|
||||
'@types/cookie@0.6.0': {}
|
||||
|
@ -3543,12 +3515,6 @@ snapshots:
|
|||
dependencies:
|
||||
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@7.8.0(typescript@5.8.3):
|
||||
|
@ -3668,8 +3634,6 @@ snapshots:
|
|||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
react@19.1.0: {}
|
||||
|
||||
read-package-json-fast@4.0.0:
|
||||
dependencies:
|
||||
json-parse-even-better-errors: 4.0.0
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
rounded="lg"
|
||||
title="Manage your certificates"
|
||||
variant="text"
|
||||
to="/cert-request"
|
||||
to="/certificates"
|
||||
/>
|
||||
</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 router from '../router'
|
||||
import vuetify from "./vuetify";
|
||||
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
|
||||
.use(vuetify)
|
||||
.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 {
|
||||
'/': RouteRecordInfo<'/', '/', 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.
|
||||
// This should be removed or at least configurable at some point, but for now, this is fine (tm)
|
||||
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());
|
||||
return http.build();
|
||||
}
|
||||
|
|
|
@ -38,6 +38,18 @@ public class CertificatesEndpoint {
|
|||
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")
|
||||
@Operation(description = "Requests a new certificate", responses = {
|
||||
@ApiResponse(responseCode = "400", description = "One of the provided parameters is invalid."),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue