From fe66a4c04c3dcae81d01db2592f463170c2628bd Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:28:34 +0200 Subject: [PATCH] Implement authentication, waitlist, and logging upgrades --- app/composables/useApi.ts | 68 +++++ app/pages/agb.vue | 107 +++++++ app/pages/api-docs.vue | 106 +++++++ app/pages/datenschutz.vue | 86 ++++++ app/pages/impressum.vue | 57 ++++ app/pages/index.vue | 190 ++++++++++-- app/pages/login.vue | 295 +++++++++++++++++++ app/pages/pm.vue | 128 +++++--- app/stores/auth.ts | 128 ++++++++ nuxt.config.ts | 10 +- server/api/atc/ptt.post.ts | 21 ++ server/api/atc/say.post.ts | 25 ++ server/api/auth/invitations.get.ts | 15 + server/api/auth/invitations.post.ts | 49 +++ server/api/auth/logout.post.ts | 10 + server/api/auth/me.get.ts | 15 + server/api/service/auth/login.post.ts | 46 +++ server/api/service/auth/refresh.post.ts | 10 + server/api/service/auth/register.post.ts | 78 +++++ server/api/service/invitations/[code].get.ts | 30 ++ server/api/service/waitlist.get.ts | 28 ++ server/api/service/waitlist.post.ts | 62 ++++ server/middleware/auth.global.ts | 17 ++ server/models/InvitationCode.ts | 26 ++ server/models/TransmissionLog.ts | 30 ++ server/models/User.ts | 32 ++ server/models/WaitlistEntry.ts | 28 ++ server/utils/auth.ts | 206 +++++++++++++ shared/utils/communicationsEngine.ts | 8 +- 29 files changed, 1847 insertions(+), 64 deletions(-) create mode 100644 app/composables/useApi.ts create mode 100644 app/pages/agb.vue create mode 100644 app/pages/api-docs.vue create mode 100644 app/pages/datenschutz.vue create mode 100644 app/pages/impressum.vue create mode 100644 app/pages/login.vue create mode 100644 app/stores/auth.ts create mode 100644 server/api/auth/invitations.get.ts create mode 100644 server/api/auth/invitations.post.ts create mode 100644 server/api/auth/logout.post.ts create mode 100644 server/api/auth/me.get.ts create mode 100644 server/api/service/auth/login.post.ts create mode 100644 server/api/service/auth/refresh.post.ts create mode 100644 server/api/service/auth/register.post.ts create mode 100644 server/api/service/invitations/[code].get.ts create mode 100644 server/api/service/waitlist.get.ts create mode 100644 server/api/service/waitlist.post.ts create mode 100644 server/middleware/auth.global.ts create mode 100644 server/models/InvitationCode.ts create mode 100644 server/models/TransmissionLog.ts create mode 100644 server/models/User.ts create mode 100644 server/models/WaitlistEntry.ts create mode 100644 server/utils/auth.ts diff --git a/app/composables/useApi.ts b/app/composables/useApi.ts new file mode 100644 index 0000000..8b78d0b --- /dev/null +++ b/app/composables/useApi.ts @@ -0,0 +1,68 @@ +import { useAuthStore } from '~/stores/auth' + +type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' + +interface ApiRequestOptions { + method?: HttpMethod + body?: T + query?: Record + headers?: HeadersInit + auth?: boolean +} + +export function useApi() { + const auth = useAuthStore() + + const execute = async (path: string, options: ApiRequestOptions = {}) => { + const { method = 'GET', body, query, headers = {}, auth: requiresAuth = true } = options + + const computedHeaders: Record = { + Accept: 'application/json', + ...(headers as Record), + } + + if (requiresAuth && auth.accessToken) { + computedHeaders.Authorization = `Bearer ${auth.accessToken}` + } + + const requestOptions: any = { + method, + headers: computedHeaders, + query, + body, + } + + if (body && !(body instanceof FormData)) { + computedHeaders['Content-Type'] = 'application/json' + requestOptions.body = body + } + + try { + return await $fetch(path, requestOptions) + } catch (error: any) { + const status = error?.status || error?.response?.status + if (status === 401 && requiresAuth) { + const refreshed = await auth.tryRefresh() + if (refreshed) { + if (auth.accessToken) { + computedHeaders.Authorization = `Bearer ${auth.accessToken}` + } + return await $fetch(path, requestOptions) + } + await auth.logout() + } + throw error + } + } + + return { + request: execute, + get: (path: string, options: ApiRequestOptions = {}) => execute(path, { ...options, method: 'GET' }), + post: (path: string, body?: any, options: ApiRequestOptions = {}) => + execute(path, { ...options, method: 'POST', body }), + put: (path: string, body?: any, options: ApiRequestOptions = {}) => + execute(path, { ...options, method: 'PUT', body }), + del: (path: string, options: ApiRequestOptions = {}) => execute(path, { ...options, method: 'DELETE' }), + } +} + diff --git a/app/pages/agb.vue b/app/pages/agb.vue new file mode 100644 index 0000000..5a2571f --- /dev/null +++ b/app/pages/agb.vue @@ -0,0 +1,107 @@ + + + + diff --git a/app/pages/api-docs.vue b/app/pages/api-docs.vue new file mode 100644 index 0000000..9a8be9d --- /dev/null +++ b/app/pages/api-docs.vue @@ -0,0 +1,106 @@ + + + + diff --git a/app/pages/datenschutz.vue b/app/pages/datenschutz.vue new file mode 100644 index 0000000..2f57350 --- /dev/null +++ b/app/pages/datenschutz.vue @@ -0,0 +1,86 @@ + + + + + + diff --git a/app/pages/impressum.vue b/app/pages/impressum.vue new file mode 100644 index 0000000..f95a6b8 --- /dev/null +++ b/app/pages/impressum.vue @@ -0,0 +1,57 @@ + + diff --git a/app/pages/index.vue b/app/pages/index.vue index a5e2964..54e6c4b 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -262,17 +262,95 @@ POST /api/route/taxi
-
-
-

Jetzt OpenSquawk ausprobieren

-

Starte kostenlos. Upgrade jederzeit. Keine Bindung.

+
+
+
+

Frühzugang & Warteliste

+

+ Trag dich ein und sichere dir deinen Platz. Aktuell warten + {{ waitlistCountDisplay }} + Pilot:innen auf den nächsten Invite-Drop. +

+
+
+
Lade Warteliste…
+ +
+

Wir senden Einladungen in Batches, priorisieren aktive Wartelistenplätze und verschicken Einladungscodes per E-Mail.

-
- - + +
+ + +