This commit is contained in:
corsacca 2026-04-21 12:35:27 +01:00
parent 6f5313f990
commit dd5255ba69
15 changed files with 80 additions and 78 deletions

View file

@ -1,7 +1,5 @@
<template>
<UApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</UApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

View file

@ -1,6 +1,10 @@
@import "tailwindcss";
@import "@nuxt/ui";
@source "../../pages/admin";
@source "../../components/admin";
@source "../../layouts/admin.vue";
@theme static {
--font-sans: 'Public Sans', sans-serif;

View file

@ -0,0 +1,6 @@
// Public site stylesheet entry point.
// Phase 1 will `@use` the ported CUBE CSS partials here
// (tokens, base/reset, composition, blocks, utilities).
//
// For Phase 0 this is intentionally empty so the public layout
// has something to import without producing CSS.

View file

@ -25,8 +25,9 @@ watch(() => route.path, () => {
</script>
<template>
<div class="min-h-screen bg-(--ui-bg) text-(--ui-text)">
<!-- Mobile top bar -->
<UApp>
<div class="min-h-screen bg-(--ui-bg) text-(--ui-text)">
<!-- Mobile top bar -->
<header class="lg:hidden bg-(--ui-bg-elevated) border-b border-(--ui-border) py-3 px-4 sticky top-0 z-40 flex items-center justify-between">
<div class="flex items-center gap-3">
<UButton
@ -126,4 +127,7 @@ watch(() => route.path, () => {
</main>
</div>
</div>
</UApp>
</template>
<style src="~/assets/css/admin.css"></style>

View file

@ -1,35 +1,5 @@
<script setup lang="ts">
const { user } = useAuth()
const config = useRuntimeConfig()
</script>
<template>
<div class="min-h-screen bg-(--ui-bg) text-(--ui-text)">
<header class="bg-(--ui-bg-elevated) border-b border-(--ui-border) py-4 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 flex justify-between items-center">
<div class="flex items-center gap-4">
<slot name="header-left">
<NuxtLink to="/" class="text-xl font-semibold hover:text-(--ui-text-muted) transition-colors">
{{ config.public.appName }}
</NuxtLink>
</slot>
</div>
<div class="flex items-center gap-3">
<slot name="header-right">
<span class="text-(--ui-text-muted) text-sm hidden sm:inline">
{{ user?.display_name || user?.email }}
</span>
<UButton to="/profile" variant="ghost" size="sm">
Profile
</UButton>
<ThemeToggle />
</slot>
</div>
</div>
</header>
<main class="max-w-7xl mx-auto px-4 py-8">
<slot />
</main>
</div>
<slot />
</template>
<style lang="scss" src="~/assets/styles/main.scss"></style>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
definePageMeta({
layout: 'admin',
middleware: 'auth'
})
</script>

View file

@ -1,34 +1,11 @@
<script setup lang="ts">
definePageMeta({
layout: false
})
const { isLoggedIn } = useAuth()
</script>
<template>
<div class="min-h-screen bg-(--ui-bg)">
<div class="flex justify-end p-4">
<ThemeToggle />
</div>
<div class="text-center py-16 px-4">
<h1 class="text-4xl font-bold text-(--ui-text) mb-4">Welcome</h1>
<p class="text-(--ui-text-muted) mb-8 max-w-lg mx-auto">
Get started by customizing this page.
</p>
<div v-if="!isLoggedIn" class="flex items-center justify-center gap-3">
<UButton to="/login" size="lg">
Sign in
</UButton>
<UButton to="/register" size="lg" variant="outline">
Create an account
</UButton>
</div>
<div v-else>
<UButton to="/dashboard" size="lg">
Go to dashboard
</UButton>
</div>
</div>
<div style="padding: 2rem; font-family: system-ui, sans-serif;">
<h1>Doxa</h1>
<p>
Marketing-rebuild scaffold. The real homepage is ported in Phase 3.
</p>
<p>
<a href="/admin">Admin →</a>
</p>
</div>
</template>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
definePageMeta({
layout: 'admin',
middleware: 'auth'
})

View file

@ -27,6 +27,7 @@
"@types/nodemailer": "^7.0.1",
"@types/nodemailer-mailgun-transport": "^1.4.6",
"eslint": "^10.2.0",
"sass": "^1.99.0",
"typescript": "^6.0.2",
"vue-tsc": "^3.2.6",
},
@ -1455,6 +1456,8 @@
"image-meta": ["image-meta@0.2.2", "", {}, "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA=="],
"immutable": ["immutable@5.1.5", "", {}, "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A=="],
"impound": ["impound@1.1.5", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "es-module-lexer": "2.0.0", "pathe": "2.0.3", "unplugin": "3.0.0", "unplugin-utils": "0.3.1" } }, "sha512-5AUn+QE0UofqNHu5f2Skf6Svukdg4ehOIq8O0EtqIx4jta0CDZYBPqpIHt0zrlUTiFVYlLpeH39DoikXBjPKpA=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
@ -1949,6 +1952,8 @@
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"sass": ["sass@1.99.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"scslre": ["scslre@0.3.0", "", { "dependencies": { "@eslint-community/regexpp": "4.12.2", "refa": "0.12.1", "regexp-ast-analysis": "0.7.1" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="],
@ -2369,6 +2374,8 @@
"rollup-plugin-visualizer/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "5.5.0", "define-lazy-prop": "3.0.0", "is-in-ssh": "1.0.0", "is-inside-container": "1.0.0", "powershell-utils": "0.1.0", "wsl-utils": "0.3.1" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
"sass/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "4.1.2" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
@ -2457,6 +2464,8 @@
"rollup-plugin-visualizer/open/wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "3.1.1", "powershell-utils": "0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
"sass/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],

View file

@ -1,6 +1,39 @@
// @ts-check
import withNuxt from './.nuxt/eslint.config.mjs'
// Guardrail: Nuxt UI components (`<U*>`) and their CSS only load inside the
// admin layout. Using a `<U*>` component on a public page would render
// unstyled because `admin.css` isn't imported there. This rule blocks that
// at lint time. Admin pages/components are exempt via overrides below.
const forbidNuxtUI = {
rules: {
'vue/no-restricted-syntax': ['error', {
selector: "VElement[rawName=/^U[A-Z]/]",
message: 'Nuxt UI components (<U*>) are only allowed inside app/pages/admin/** and app/components/admin/**. Public pages must use ported SCSS components.'
}]
}
}
export default withNuxt(
// Your custom configs here
{
files: ['app/pages/**/*.vue', 'app/components/**/*.vue', 'app/layouts/**/*.vue'],
...forbidNuxtUI
},
{
files: [
'app/pages/admin/**/*.vue',
'app/components/admin/**/*.vue',
'app/components/ThemeToggle.vue',
'app/layouts/admin.vue',
'app/layouts/auth.vue',
'app/pages/login.vue',
'app/pages/register.vue',
'app/pages/reset-password.vue',
'app/pages/dashboard.vue',
'app/pages/profile.vue'
],
rules: {
'vue/no-restricted-syntax': 'off'
}
}
)

View file

@ -15,8 +15,6 @@ export default defineNuxtConfig({
ssr: false,
css: ['~/assets/css/main.css'],
ui: {
theme: {
colors: ['primary', 'secondary', 'info', 'success', 'warning', 'error', 'neutral']

View file

@ -33,6 +33,7 @@
"@types/nodemailer": "^7.0.1",
"@types/nodemailer-mailgun-transport": "^1.4.6",
"eslint": "^10.2.0",
"sass": "^1.99.0",
"typescript": "^6.0.2",
"vue-tsc": "^3.2.6"
}

View file

@ -37,7 +37,7 @@ export default defineEventHandler(async (event) => {
.selectAll()
.where('token', '=', sanitizedToken)
.where('used', '=', false)
.where('expires', '>', new Date().toISOString())
.where('expires', '>', new Date())
.executeTakeFirst()
if (!resetRequest) {
@ -72,7 +72,7 @@ export default defineEventHandler(async (event) => {
.where((eb) =>
eb.or([
eb('used', '=', true),
eb('expires', '<=', new Date().toISOString()),
eb('expires', '<=', new Date()),
])
)
.execute()

View file

@ -2,7 +2,7 @@ import { db } from './database'
import crypto from 'crypto'
import type { H3Event } from 'h3'
import type { Kysely } from 'kysely'
import type { Database } from '~/server/database/schema'
import type { Database } from '../database/schema'
import { getAuthUser } from './auth'
interface LogEventOptions {

View file

@ -1,7 +1,7 @@
import { Kysely, sql } from 'kysely'
import { PostgresJSDialect } from 'kysely-postgres-js'
import postgres from 'postgres'
import type { Database } from '~/server/database/schema'
import type { Database } from '../database/schema'
let _db: Kysely<Database> | null = null