feat: authentication
This commit is contained in:
@@ -1 +1,3 @@
|
|||||||
DATABASE_URL=sqlite.db
|
DATABASE_URL=sqlite.db
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=strong_password
|
||||||
|
|||||||
7
app/middleware/auth.ts
Normal file
7
app/middleware/auth.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default defineNuxtRouteMiddleware(() => {
|
||||||
|
const { loggedIn } = useUserSession();
|
||||||
|
|
||||||
|
if (!loggedIn.value) {
|
||||||
|
return navigateTo("/auth/sign-in");
|
||||||
|
}
|
||||||
|
});
|
||||||
59
app/pages/auth/sign-in.vue
Normal file
59
app/pages/auth/sign-in.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { z } from "zod";
|
||||||
|
import type { AuthFormField, FormSubmitEvent } from "@nuxt/ui";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: () => {
|
||||||
|
const { loggedIn } = useUserSession();
|
||||||
|
if (loggedIn.value) return navigateTo("/");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fields: AuthFormField[] = [
|
||||||
|
{ name: "username", type: "text", label: "Username", required: true },
|
||||||
|
{ name: "password", type: "password", label: "Password", required: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
username: z.string({ error: "Required" }),
|
||||||
|
password: z.string({ error: "Required" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
type Schema = z.output<typeof schema>;
|
||||||
|
|
||||||
|
const { fetch: fetchSession } = useUserSession();
|
||||||
|
const error = ref<string | null>(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const onSubmit = async (event: FormSubmitEvent<Schema>) => {
|
||||||
|
error.value = null;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
await $fetch("/api/auth/sign-in", { method: "POST", body: event.data });
|
||||||
|
await fetchSession();
|
||||||
|
await navigateTo("/");
|
||||||
|
} catch (e) {
|
||||||
|
error.value = getApiError(e);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center justify-center min-h-screen">
|
||||||
|
<UPageCard class="w-full max-w-sm">
|
||||||
|
<UAuthForm
|
||||||
|
title="Sign In"
|
||||||
|
:fields="fields"
|
||||||
|
:schema="schema"
|
||||||
|
:loading="loading"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<template v-if="error" #validation>
|
||||||
|
<UAlert color="error" icon="i-lucide-circle-alert" :title="error" />
|
||||||
|
</template>
|
||||||
|
</UAuthForm>
|
||||||
|
</UPageCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LazyLinkModal } from "#components";
|
import { LazyLinkModal } from "#components";
|
||||||
import type { InternalApi } from "nitropack/types";
|
|
||||||
|
|
||||||
type Link = InternalApi["/api/links"]["get"][number];
|
definePageMeta({ middleware: "auth" });
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const overlay = useOverlay();
|
const overlay = useOverlay();
|
||||||
|
|
||||||
|
const { fetch: fetchSession } = useUserSession();
|
||||||
|
|
||||||
|
const signOut = async () => {
|
||||||
|
await $fetch("/api/auth/sign-out", { method: "POST" });
|
||||||
|
await fetchSession();
|
||||||
|
await navigateTo("/auth/sign-in");
|
||||||
|
};
|
||||||
|
|
||||||
const { data: links, status, refresh } = useLazyFetch("/api/links", { key: "links", server: false, });
|
const { data: links, status, refresh } = useLazyFetch("/api/links", { key: "links", server: false, });
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -64,6 +71,11 @@ const deleteLink = async (link: Link) => {
|
|||||||
]"
|
]"
|
||||||
class="px-2"
|
class="px-2"
|
||||||
/>
|
/>
|
||||||
|
<template #footer>
|
||||||
|
<UButton variant="ghost" icon="i-lucide-log-out" block @click="signOut">
|
||||||
|
Sign out
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
</UDashboardSidebar>
|
</UDashboardSidebar>
|
||||||
|
|
||||||
<UDashboardPanel>
|
<UDashboardPanel>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
modules: [
|
modules: ['@nuxt/eslint', '@nuxt/ui', 'nuxt-auth-utils'],
|
||||||
'@nuxt/eslint',
|
|
||||||
'@nuxt/ui'
|
|
||||||
],
|
|
||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
@@ -19,7 +16,7 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
css: ['~/assets/css/main.css'],
|
css: ['~/assets/css/main.css'],
|
||||||
|
|
||||||
compatibilityDate: '2025-01-15',
|
compatibilityDate: '2025-01-15',
|
||||||
|
|
||||||
eslint: {
|
eslint: {
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"nuxt": "^4.4.2",
|
"nuxt": "^4.4.2",
|
||||||
|
"nuxt-auth-utils": "0.5.29",
|
||||||
"tailwindcss": "^4.2.1",
|
"tailwindcss": "^4.2.1",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
|
|||||||
142
pnpm-lock.yaml
generated
142
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
|||||||
nuxt:
|
nuxt:
|
||||||
specifier: ^4.4.2
|
specifier: ^4.4.2
|
||||||
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.8.0)(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0)))(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2)
|
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.8.0)(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0)))(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2)
|
||||||
|
nuxt-auth-utils:
|
||||||
|
specifier: 0.5.29
|
||||||
|
version: 0.5.29(magicast@0.5.2)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.2.1
|
version: 4.2.1
|
||||||
@@ -54,6 +57,18 @@ importers:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@adonisjs/hash@9.1.1':
|
||||||
|
resolution: {integrity: sha512-ZkRguwjAp4skKvKDdRAfdJ2oqQ0N7p9l3sioyXO1E8o0WcsyDgEpsTQtuVNoIdMiw4sn4gJlmL3nyF4BcK1ZDQ==}
|
||||||
|
engines: {node: '>=20.6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
argon2: ^0.31.2 || ^0.41.0 || ^0.43.0
|
||||||
|
bcrypt: ^5.1.1 || ^6.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
argon2:
|
||||||
|
optional: true
|
||||||
|
bcrypt:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0':
|
'@alloc/quick-lru@5.2.0':
|
||||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1485,6 +1500,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
|
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
|
'@phc/format@1.0.0':
|
||||||
|
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -1501,6 +1520,17 @@ packages:
|
|||||||
'@poppinss/exception@1.2.3':
|
'@poppinss/exception@1.2.3':
|
||||||
resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==}
|
resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==}
|
||||||
|
|
||||||
|
'@poppinss/object-builder@1.1.0':
|
||||||
|
resolution: {integrity: sha512-FOrOq52l7u8goR5yncX14+k+Ewi5djnrt1JwXeS/FvnwAPOiveFhiczCDuvXdssAwamtrV2hp5Rw9v+n2T7hQg==}
|
||||||
|
engines: {node: '>=20.6.0'}
|
||||||
|
|
||||||
|
'@poppinss/string@1.7.1':
|
||||||
|
resolution: {integrity: sha512-OrLzv/nGDU6l6dLXIQHe8nbNSWWfuSbpB/TW5nRpZFf49CLuQlIHlSPN9IdSUv2vG+59yGM6LoibsaHn8B8mDw==}
|
||||||
|
|
||||||
|
'@poppinss/utils@6.10.1':
|
||||||
|
resolution: {integrity: sha512-da+MMyeXhBaKtxQiWPfy7+056wk3lVIhioJnXHXkJ2/OHDaZfFcyKHNl1R06sdYO8lIRXcXdoZ6LO2ARmkAREA==}
|
||||||
|
engines: {node: '>=18.16.0'}
|
||||||
|
|
||||||
'@remirror/core-constants@3.0.0':
|
'@remirror/core-constants@3.0.0':
|
||||||
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
|
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
|
||||||
|
|
||||||
@@ -2106,6 +2136,9 @@ packages:
|
|||||||
'@types/node@25.5.0':
|
'@types/node@25.5.0':
|
||||||
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
|
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
|
||||||
|
|
||||||
|
'@types/pluralize@0.0.33':
|
||||||
|
resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==}
|
||||||
|
|
||||||
'@types/resolve@1.20.2':
|
'@types/resolve@1.20.2':
|
||||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||||
|
|
||||||
@@ -2687,6 +2720,10 @@ packages:
|
|||||||
caniuse-lite@1.0.30001779:
|
caniuse-lite@1.0.30001779:
|
||||||
resolution: {integrity: sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==}
|
resolution: {integrity: sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==}
|
||||||
|
|
||||||
|
case-anything@3.1.2:
|
||||||
|
resolution: {integrity: sha512-wljhAjDDIv/hM2FzgJnYQg90AWmZMNtESCjTeLH680qTzdo0nErlCxOmgzgX4ZsZAtIvqHyD87ES8QyriXB+BQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
change-case@5.4.4:
|
change-case@5.4.4:
|
||||||
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
||||||
|
|
||||||
@@ -3421,6 +3458,10 @@ packages:
|
|||||||
flatted@3.4.1:
|
flatted@3.4.1:
|
||||||
resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==}
|
resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==}
|
||||||
|
|
||||||
|
flattie@1.1.1:
|
||||||
|
resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
fontaine@0.8.0:
|
fontaine@0.8.0:
|
||||||
resolution: {integrity: sha512-eek1GbzOdWIj9FyQH/emqW1aEdfC3lYRCHepzwlFCm5T77fBSRSyNRKE6/antF1/B1M+SfJXVRQTY9GAr7lnDg==}
|
resolution: {integrity: sha512-eek1GbzOdWIj9FyQH/emqW1aEdfC3lYRCHepzwlFCm5T77fBSRSyNRKE6/antF1/B1M+SfJXVRQTY9GAr7lnDg==}
|
||||||
engines: {node: '>=18.12.0'}
|
engines: {node: '>=18.12.0'}
|
||||||
@@ -3723,6 +3764,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jose@6.2.2:
|
||||||
|
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@@ -4191,6 +4235,23 @@ packages:
|
|||||||
nth-check@2.1.1:
|
nth-check@2.1.1:
|
||||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
|
||||||
|
nuxt-auth-utils@0.5.29:
|
||||||
|
resolution: {integrity: sha512-aQ9oD8QR51jUCe2BFEsO/G/E0K+XUy8Skjn3hYcNLiqZ9XE4Y3uT/ozBCmAQ+TR3WW6prj8vUR0wQes8W1N0PA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@atproto/api': ^0.13.15
|
||||||
|
'@atproto/oauth-client-node': ^0.2.0
|
||||||
|
'@simplewebauthn/browser': ^11.0.0
|
||||||
|
'@simplewebauthn/server': ^11.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@atproto/api':
|
||||||
|
optional: true
|
||||||
|
'@atproto/oauth-client-node':
|
||||||
|
optional: true
|
||||||
|
'@simplewebauthn/browser':
|
||||||
|
optional: true
|
||||||
|
'@simplewebauthn/server':
|
||||||
|
optional: true
|
||||||
|
|
||||||
nuxt@4.4.2:
|
nuxt@4.4.2:
|
||||||
resolution: {integrity: sha512-iWVFpr/YEqVU/CenqIHMnIkvb2HE/9f+q8oxZ+pj2et+60NljGRClCgnmbvGPdmNFE0F1bEhoBCYfqbDOCim3Q==}
|
resolution: {integrity: sha512-iWVFpr/YEqVU/CenqIHMnIkvb2HE/9f+q8oxZ+pj2et+60NljGRClCgnmbvGPdmNFE0F1bEhoBCYfqbDOCim3Q==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
@@ -4209,6 +4270,9 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
oauth4webapi@3.8.5:
|
||||||
|
resolution: {integrity: sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==}
|
||||||
|
|
||||||
object-deep-merge@2.0.0:
|
object-deep-merge@2.0.0:
|
||||||
resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==}
|
resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==}
|
||||||
|
|
||||||
@@ -4247,6 +4311,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
openid-client@6.8.2:
|
||||||
|
resolution: {integrity: sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==}
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -4760,6 +4827,10 @@ packages:
|
|||||||
safe-buffer@5.2.1:
|
safe-buffer@5.2.1:
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
|
safe-stable-stringify@2.5.0:
|
||||||
|
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
sax@1.5.0:
|
sax@1.5.0:
|
||||||
resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==}
|
resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==}
|
||||||
engines: {node: '>=11.0.0'}
|
engines: {node: '>=11.0.0'}
|
||||||
@@ -4771,6 +4842,9 @@ packages:
|
|||||||
scule@1.3.0:
|
scule@1.3.0:
|
||||||
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
||||||
|
|
||||||
|
secure-json-parse@4.1.0:
|
||||||
|
resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
|
||||||
|
|
||||||
semver@6.3.1:
|
semver@6.3.1:
|
||||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -4837,6 +4911,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
|
resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
slugify@1.6.8:
|
||||||
|
resolution: {integrity: sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
smob@1.6.1:
|
smob@1.6.1:
|
||||||
resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==}
|
resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
@@ -5533,6 +5611,11 @@ packages:
|
|||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@adonisjs/hash@9.1.1':
|
||||||
|
dependencies:
|
||||||
|
'@phc/format': 1.0.0
|
||||||
|
'@poppinss/utils': 6.10.1
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0': {}
|
'@alloc/quick-lru@5.2.0': {}
|
||||||
|
|
||||||
'@antfu/install-pkg@1.1.0':
|
'@antfu/install-pkg@1.1.0':
|
||||||
@@ -7006,6 +7089,8 @@ snapshots:
|
|||||||
'@parcel/watcher-win32-ia32': 2.5.6
|
'@parcel/watcher-win32-ia32': 2.5.6
|
||||||
'@parcel/watcher-win32-x64': 2.5.6
|
'@parcel/watcher-win32-x64': 2.5.6
|
||||||
|
|
||||||
|
'@phc/format@1.0.0': {}
|
||||||
|
|
||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -7023,6 +7108,24 @@ snapshots:
|
|||||||
|
|
||||||
'@poppinss/exception@1.2.3': {}
|
'@poppinss/exception@1.2.3': {}
|
||||||
|
|
||||||
|
'@poppinss/object-builder@1.1.0': {}
|
||||||
|
|
||||||
|
'@poppinss/string@1.7.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/pluralize': 0.0.33
|
||||||
|
case-anything: 3.1.2
|
||||||
|
pluralize: 8.0.0
|
||||||
|
slugify: 1.6.8
|
||||||
|
|
||||||
|
'@poppinss/utils@6.10.1':
|
||||||
|
dependencies:
|
||||||
|
'@poppinss/exception': 1.2.3
|
||||||
|
'@poppinss/object-builder': 1.1.0
|
||||||
|
'@poppinss/string': 1.7.1
|
||||||
|
flattie: 1.1.1
|
||||||
|
safe-stable-stringify: 2.5.0
|
||||||
|
secure-json-parse: 4.1.0
|
||||||
|
|
||||||
'@remirror/core-constants@3.0.0': {}
|
'@remirror/core-constants@3.0.0': {}
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-rc.2': {}
|
'@rolldown/pluginutils@1.0.0-rc.2': {}
|
||||||
@@ -7539,6 +7642,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.18.2
|
undici-types: 7.18.2
|
||||||
|
|
||||||
|
'@types/pluralize@0.0.33': {}
|
||||||
|
|
||||||
'@types/resolve@1.20.2': {}
|
'@types/resolve@1.20.2': {}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.20': {}
|
'@types/web-bluetooth@0.0.20': {}
|
||||||
@@ -8151,6 +8256,8 @@ snapshots:
|
|||||||
|
|
||||||
caniuse-lite@1.0.30001779: {}
|
caniuse-lite@1.0.30001779: {}
|
||||||
|
|
||||||
|
case-anything@3.1.2: {}
|
||||||
|
|
||||||
change-case@5.4.4: {}
|
change-case@5.4.4: {}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
@@ -8859,6 +8966,8 @@ snapshots:
|
|||||||
|
|
||||||
flatted@3.4.1: {}
|
flatted@3.4.1: {}
|
||||||
|
|
||||||
|
flattie@1.1.1: {}
|
||||||
|
|
||||||
fontaine@0.8.0:
|
fontaine@0.8.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@capsizecss/unpack': 4.0.0
|
'@capsizecss/unpack': 4.0.0
|
||||||
@@ -9166,6 +9275,8 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.6.1: {}
|
jiti@2.6.1: {}
|
||||||
|
|
||||||
|
jose@6.2.2: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-tokens@9.0.1: {}
|
js-tokens@9.0.1: {}
|
||||||
@@ -9649,6 +9760,24 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
boolbase: 1.0.0
|
boolbase: 1.0.0
|
||||||
|
|
||||||
|
nuxt-auth-utils@0.5.29(magicast@0.5.2):
|
||||||
|
dependencies:
|
||||||
|
'@adonisjs/hash': 9.1.1
|
||||||
|
'@nuxt/kit': 4.4.2(magicast@0.5.2)
|
||||||
|
defu: 6.1.4
|
||||||
|
h3: 1.15.6
|
||||||
|
hookable: 6.1.0
|
||||||
|
jose: 6.2.2
|
||||||
|
ofetch: 1.5.1
|
||||||
|
openid-client: 6.8.2
|
||||||
|
pathe: 2.0.3
|
||||||
|
scule: 1.3.0
|
||||||
|
uncrypto: 0.1.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- argon2
|
||||||
|
- bcrypt
|
||||||
|
- magicast
|
||||||
|
|
||||||
nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.8.0)(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0)))(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2):
|
nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.8.0)(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0)))(drizzle-orm@0.45.1(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3)
|
'@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3)
|
||||||
@@ -9785,6 +9914,8 @@ snapshots:
|
|||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
tinyexec: 1.0.4
|
tinyexec: 1.0.4
|
||||||
|
|
||||||
|
oauth4webapi@3.8.5: {}
|
||||||
|
|
||||||
object-deep-merge@2.0.0: {}
|
object-deep-merge@2.0.0: {}
|
||||||
|
|
||||||
obug@2.1.1: {}
|
obug@2.1.1: {}
|
||||||
@@ -9826,6 +9957,11 @@ snapshots:
|
|||||||
is-docker: 2.2.1
|
is-docker: 2.2.1
|
||||||
is-wsl: 2.2.0
|
is-wsl: 2.2.0
|
||||||
|
|
||||||
|
openid-client@6.8.2:
|
||||||
|
dependencies:
|
||||||
|
jose: 6.2.2
|
||||||
|
oauth4webapi: 3.8.5
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
@@ -10452,6 +10588,8 @@ snapshots:
|
|||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
|
safe-stable-stringify@2.5.0: {}
|
||||||
|
|
||||||
sax@1.5.0: {}
|
sax@1.5.0: {}
|
||||||
|
|
||||||
scslre@0.3.0:
|
scslre@0.3.0:
|
||||||
@@ -10462,6 +10600,8 @@ snapshots:
|
|||||||
|
|
||||||
scule@1.3.0: {}
|
scule@1.3.0: {}
|
||||||
|
|
||||||
|
secure-json-parse@4.1.0: {}
|
||||||
|
|
||||||
semver@6.3.1: {}
|
semver@6.3.1: {}
|
||||||
|
|
||||||
semver@7.7.4: {}
|
semver@7.7.4: {}
|
||||||
@@ -10539,6 +10679,8 @@ snapshots:
|
|||||||
|
|
||||||
slash@5.1.0: {}
|
slash@5.1.0: {}
|
||||||
|
|
||||||
|
slugify@1.6.8: {}
|
||||||
|
|
||||||
smob@1.6.1: {}
|
smob@1.6.1: {}
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
|
|||||||
17
server/api/auth/sign-in.post.ts
Normal file
17
server/api/auth/sign-in.post.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { env } from "#server/env";
|
||||||
|
|
||||||
|
const bodySchema = z.object({
|
||||||
|
username: z.string(),
|
||||||
|
password: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readValidatedBody(event, bodySchema.parse);
|
||||||
|
|
||||||
|
if (body.username !== env.ADMIN_USERNAME || body.password !== env.ADMIN_PASSWORD) {
|
||||||
|
throw createError({ statusCode: 401, message: "Invalid credentials" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await setUserSession(event, { user: { username: body.username } });
|
||||||
|
});
|
||||||
3
server/api/auth/sign-out.post.ts
Normal file
3
server/api/auth/sign-out.post.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
await clearUserSession(event);
|
||||||
|
});
|
||||||
@@ -3,6 +3,8 @@ import { z } from 'zod'
|
|||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
DATABASE_URL: z.string().min(1),
|
DATABASE_URL: z.string().min(1),
|
||||||
|
ADMIN_USERNAME: z.string().min(1).default("admin"),
|
||||||
|
ADMIN_PASSWORD: z.string().min(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const env = schema.parse(process.env)
|
export const env = schema.parse(process.env)
|
||||||
|
|||||||
7
server/middleware/auth.ts
Normal file
7
server/middleware/auth.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const path = getRequestURL(event).pathname;
|
||||||
|
|
||||||
|
if (path.startsWith("/api/") && !path.startsWith("/api/auth/")) {
|
||||||
|
await requireUserSession(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user