diff --git a/nuxt.config.ts b/nuxt.config.ts index 317394a..b17f8df 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -52,10 +52,11 @@ export default defineNuxtConfig({ discordTesterRoleId: '', discordSupporterRoleId: '', discourseSsoSecret: '', + apiBase: 'http://localhost:8056', + apiBaseHost: 'api.pretendo.cc', public: { baseUrl: 'https://pretendo.network', - apiBase: 'https://api.pretendo.cc', hcaptchaSiteKey: '', cookieSecure: false } diff --git a/server/api/account/update.patch.ts b/server/api/account/update.patch.ts index 70c365f..865c059 100644 --- a/server/api/account/update.patch.ts +++ b/server/api/account/update.patch.ts @@ -2,17 +2,18 @@ import { AccountUpdateSchema } from '~~/shared/api-types'; export default defineEventHandler(async (event): Promise => { const body = await readZodBody(event, AccountUpdateSchema); - const apiFetch = useHttpApi(event); const auth = enforceLoggedIn(event); + const apiFetch = useHttpApi(event, auth.token); // There's no equivalent GRPC endpoint to use, so we're using the HTTP api await apiFetch('/v1/user', { - headers: { - Authorization: `Bearer ${auth.token}` - }, - body: { + method: 'POST', + body: JSON.stringify({ mii: body.mii, environment: body.environment + }), + headers: { + 'Content-type': 'application/json' } }); }); diff --git a/server/api/auth/me.get.ts b/server/api/auth/me.get.ts index c9a71cc..d6451b4 100644 --- a/server/api/auth/me.get.ts +++ b/server/api/auth/me.get.ts @@ -8,6 +8,8 @@ export default defineEventHandler(async (event): Promise => { return { pid: data.pid, username: data.username, + accessLevel: data.accessLevel, + serverAccessLevel: data.serverAccessLevel as any, mii: data.mii ? { imageUrl: `https://r2-cdn.pretendo.cc/mii/${data.pid}/normal_face.png`, diff --git a/server/utils/httpApi.ts b/server/utils/httpApi.ts index 41ed96d..4e539ef 100644 --- a/server/utils/httpApi.ts +++ b/server/utils/httpApi.ts @@ -1,6 +1,21 @@ -export function useHttpApi(event: H3Event) { +import { request } from 'undici'; +import type { Dispatcher } from 'undici'; + +export function useHttpApi(event: H3Event, token?: string) { const config = useRuntimeConfig(event); - return $fetch.create({ - baseURL: config.public.apiBase - }); + + return (url: string, opts: { headers?: HeadersInit; body?: string; method?: Dispatcher.HttpMethod } = {}) => { + const headers = new Headers(opts.headers); + headers.set('Host', config.apiBaseHost); // Can't use fetch due to Host header overwriting + if (token) { + headers.set('Authorization', `Bearer ${token}`); + } + + const urlWithBase = new URL(url, config.apiBase); + return request(urlWithBase, { + method: opts.method, + headers: Object.fromEntries(headers.entries()), + body: opts?.body + }); + }; } diff --git a/shared/api-types.ts b/shared/api-types.ts index bb1934a..739f163 100644 --- a/shared/api-types.ts +++ b/shared/api-types.ts @@ -3,6 +3,8 @@ import { z } from 'zod'; export type GetApiAuthMe = { pid: number; username: string; + accessLevel: number; + serverAccessLevel: 'dev' | 'test' | 'prod'; mii: { imageUrl: string; name: string; diff --git a/src/composables/apiFetch.ts b/src/composables/apiFetch.ts new file mode 100644 index 0000000..c867064 --- /dev/null +++ b/src/composables/apiFetch.ts @@ -0,0 +1,17 @@ +export const apiFetch = $fetch.create({ + onRequest({ options }) { + const token = useAuthStore().getToken(); + if (token) { + options.headers.set('Authorization', 'Bearer ' + token); + } + }, + async onResponseError({ response }) { + if (response.status === 401) { + await navigateTo('/account/login'); + } + } +}); + +export const useApiFetch = createUseFetch({ + $fetch: apiFetch +}); diff --git a/src/plugins/types.d.ts b/src/plugins/types.d.ts index 9f6b68e..800f68e 100644 --- a/src/plugins/types.d.ts +++ b/src/plugins/types.d.ts @@ -34,13 +34,14 @@ declare module 'nuxt/schema' { discordSupporterRoleId: string; discourseSsoSecret: string; + + apiBase: string; + apiBaseHost: string; } interface PublicRuntimeConfig { baseUrl: string; - apiBase: string; cookieSecure: boolean; - hcaptchaSiteKey: string; } }