diff --git a/astro.config.mjs b/astro.config.mjs index 2200e93..a7248d8 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -6,20 +6,25 @@ import remarkMath from 'remark-math' import yaml from '@rollup/plugin-yaml' +import node from '@astrojs/node' + // https://astro.build/config export default defineConfig({ vite: { plugins: [yaml()], }, + server: { port: 3000, }, + markdown: { remarkPlugins: [remarkMath], shikiConfig: { theme: 'github-light', }, }, + integrations: [ preact({ compat: true, @@ -28,5 +33,10 @@ export default defineConfig({ remarkPlugins: [remarkMath], }), ], - output: 'static', + + output: 'server', + + adapter: node({ + mode: 'standalone', + }), }) diff --git a/bun.lockb b/bun.lockb index a74191b..507ac3e 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..cc9c855 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,11 @@ +import 'dotenv/config' +import { defineConfig } from 'drizzle-kit' + +export default defineConfig({ + out: './drizzle', + schema: './src/db/schema.ts', + dialect: 'sqlite', + dbCredentials: { + url: process.env.DB_FILE_NAME!, + }, +}) diff --git a/drizzle/0000_violet_agent_zero.sql b/drizzle/0000_violet_agent_zero.sql new file mode 100644 index 0000000..86e0ba6 --- /dev/null +++ b/drizzle/0000_violet_agent_zero.sql @@ -0,0 +1,82 @@ +CREATE TABLE `accounts` ( + `user_id` text, + `provider` text NOT NULL, + `username` text NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `appunti` ( + `user_id` text, + `hash` text NOT NULL, + `link` text, + `visibility` text NOT NULL, + `title` text NOT NULL, + `description` text, + `tags` text NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `poisson_requests` ( + `user_id` text, + `request_type` text NOT NULL, + `comments` text, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `users` ( + `id` text, + `username` text NOT NULL, + `full_name` text, + `email` text, + FOREIGN KEY (`id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `account` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `provider_id` text NOT NULL, + `user_id` text NOT NULL, + `access_token` text, + `refresh_token` text, + `id_token` text, + `access_token_expires_at` integer, + `refresh_token_expires_at` integer, + `scope` text, + `password` text, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `session` ( + `id` text PRIMARY KEY NOT NULL, + `expires_at` integer NOT NULL, + `token` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + `ip_address` text, + `user_agent` text, + `user_id` text NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint +CREATE TABLE `user` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `email` text NOT NULL, + `email_verified` integer NOT NULL, + `image` text, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);--> statement-breakpoint +CREATE TABLE `verification` ( + `id` text PRIMARY KEY NOT NULL, + `identifier` text NOT NULL, + `value` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer, + `updated_at` integer +); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..7c6944a --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,555 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "84352768-6c24-4412-9d9e-8a3c756d31b1", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "accounts_user_id_user_id_fk": { + "name": "accounts_user_id_user_id_fk", + "tableFrom": "accounts", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "appunti": { + "name": "appunti", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "appunti_user_id_users_id_fk": { + "name": "appunti_user_id_users_id_fk", + "tableFrom": "appunti", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "poisson_requests": { + "name": "poisson_requests", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_type": { + "name": "request_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comments": { + "name": "comments", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "poisson_requests_user_id_users_id_fk": { + "name": "poisson_requests_user_id_users_id_fk", + "tableFrom": "poisson_requests", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_id_user_id_fk": { + "name": "users_id_user_id_fk", + "tableFrom": "users", + "tableTo": "user", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..913f43e --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1749233567397, + "tag": "0000_violet_agent_zero", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index bf80598..8aa0c01 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,16 @@ "@fontsource/source-code-pro": "^5.0.16", "@fontsource/source-sans-pro": "^5.0.8", "@fontsource/space-mono": "^5.0.20", + "@libsql/client": "^0.15.8", "@phosphor-icons/core": "^2.1.1", "@phosphor-icons/react": "^2.1.7", "@preact/signals": "^1.3.0", "@types/jsdom": "^21.1.7", "astro": "5.1.0", + "better-auth": "^1.2.8", + "better-sqlite3": "^11.10.0", + "dotenv": "^16.5.0", + "drizzle-orm": "^0.44.2", "fuse.js": "^7.0.0", "katex": "^0.16.9", "lucide-static": "^0.468.0", @@ -36,6 +41,7 @@ "@astrojs/mdx": "4.0.2", "@rollup/plugin-yaml": "^4.1.2", "@types/katex": "^0.16.7", + "drizzle-kit": "^0.31.1", "jsdom": "^24.1.1", "linkedom": "^0.18.4", "npm-run-all": "^4.1.5", diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..bfde777 --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,28 @@ +import { betterAuth } from 'better-auth' +import { genericOAuth } from 'better-auth/plugins' +import { drizzleAdapter } from 'better-auth/adapters/drizzle' +import * as authSchema from '@/db/auth-schema' +import db from '@/db' + +export const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: 'sqlite', + schema: authSchema, + }), + plugins: [ + genericOAuth({ + config: [ + { + providerId: 'unipi', + clientId: process.env.OAUTH_CLIENT_ID!, + clientSecret: process.env.OAUTH_CLIENT_SECRET!, + scopes: process.env.OAUTH_SCOPES!.split(' '), + userInfoUrl: process.env.OAUTH_USER_INFO_URL!, + authorizationUrl: process.env.OAUTH_AUTH_URL!, + tokenUrl: `${process.env.OAUTH_TOKEN_HOST}${process.env.OAUTH_TOKEN_PATH}`, + redirectURI: process.env.OAUTH_REDIRECT_URL!, + }, + ], + }), + ], +}) diff --git a/src/client/auth-client.ts b/src/client/auth-client.ts new file mode 100644 index 0000000..a134d33 --- /dev/null +++ b/src/client/auth-client.ts @@ -0,0 +1,5 @@ +import { createAuthClient } from 'better-auth/client' +import { genericOAuthClient } from 'better-auth/client/plugins' +export const authClient = createAuthClient({ + plugins: [genericOAuthClient()], +}) diff --git a/src/db/auth-schema.ts b/src/db/auth-schema.ts new file mode 100644 index 0000000..f527aa8 --- /dev/null +++ b/src/db/auth-schema.ts @@ -0,0 +1,57 @@ +import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core' + +export const user = sqliteTable('user', { + id: text('id').primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + emailVerified: integer('email_verified', { mode: 'boolean' }) + .$defaultFn(() => false) + .notNull(), + image: text('image'), + createdAt: integer('created_at', { mode: 'timestamp' }) + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp' }) + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), +}) + +export const session = sqliteTable('session', { + id: text('id').primaryKey(), + expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(), + token: text('token').notNull().unique(), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), +}) + +export const account = sqliteTable('account', { + id: text('id').primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: integer('access_token_expires_at', { mode: 'timestamp' }), + refreshTokenExpiresAt: integer('refresh_token_expires_at', { mode: 'timestamp' }), + scope: text('scope'), + password: text('password'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(), +}) + +export const verification = sqliteTable('verification', { + id: text('id').primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => /* @__PURE__ */ new Date()), + updatedAt: integer('updated_at', { mode: 'timestamp' }).$defaultFn(() => /* @__PURE__ */ new Date()), +}) diff --git a/src/db/index.ts b/src/db/index.ts new file mode 100644 index 0000000..a3ce19f --- /dev/null +++ b/src/db/index.ts @@ -0,0 +1,4 @@ +import 'dotenv/config' +import { drizzle } from 'drizzle-orm/libsql' + +export default drizzle(process.env.DB_FILE_NAME!) diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100644 index 0000000..c5315bf --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,33 @@ +import { sqliteTable, text } from 'drizzle-orm/sqlite-core' +export * from './auth-schema' + +import { user } from './auth-schema' + +export const usersTable = sqliteTable('users', { + id: text('id').references(() => user.id), + username: text('username').notNull(), + fullName: text('full_name'), + email: text('email'), +}) + +export const accountsTable = sqliteTable('accounts', { + userId: text('user_id').references(() => user.id), + provider: text('provider').notNull(), + username: text('username').notNull(), +}) + +export const poissonRequestsTable = sqliteTable('poisson_requests', { + userId: text('user_id').references(() => usersTable.id), + requestType: text('request_type').notNull(), // "link" or "create" + comments: text('comments'), // contains username suggestions +}) + +export const appuntiTable = sqliteTable('appunti', { + userId: text('user_id').references(() => usersTable.id), + hash: text('hash').notNull(), + link: text('link'), + visibility: text('visibility').notNull(), // "public", "internal", "private" + title: text('title').notNull(), + description: text('description'), + tags: text('tags').notNull(), // Array of strings, e.g. ["#geometria-2", "#511aa", "#2017-18", "#frigerio"] +}) diff --git a/src/env.d.ts b/src/env.d.ts index acef35f..7691f9f 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,2 +1,10 @@ /// /// + +declare namespace App { + // Note: 'import {} from ""' syntax does not work in .d.ts files. + interface Locals { + user: import('better-auth').User | null + session: import('better-auth').Session | null + } +} diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..e5f1b1f --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,18 @@ +import { auth } from '@/auth' +import { defineMiddleware } from 'astro:middleware' + +export const onRequest = defineMiddleware(async (context, next) => { + const isAuthed = await auth.api.getSession({ + headers: context.request.headers, + }) + + if (isAuthed) { + context.locals.user = isAuthed.user + context.locals.session = isAuthed.session + } else { + context.locals.user = null + context.locals.session = null + } + + return next() +}) diff --git a/src/pages/api/auth/[...all].ts b/src/pages/api/auth/[...all].ts new file mode 100644 index 0000000..04b18eb --- /dev/null +++ b/src/pages/api/auth/[...all].ts @@ -0,0 +1,8 @@ +import { auth } from '@/auth' +import type { APIRoute } from 'astro' + +export const ALL: APIRoute = async ctx => { + // If you want to use rate limiting, make sure to set the 'x-forwarded-for' header to the request headers from the context + // ctx.request.headers.set("x-forwarded-for", ctx.clientAddress); + return auth.handler(ctx.request) +} diff --git a/src/pages/login.astro b/src/pages/login.astro index 7a2a4a0..fbbf60a 100644 --- a/src/pages/login.astro +++ b/src/pages/login.astro @@ -19,4 +19,13 @@ import PageLayout from '../layouts/PageLayout.astro' Login + + diff --git a/src/pages/profilo.astro b/src/pages/profilo.astro new file mode 100644 index 0000000..55bc529 --- /dev/null +++ b/src/pages/profilo.astro @@ -0,0 +1,12 @@ +--- +const session = () => { + if (Astro.locals.session) { + return Astro.locals.session + } else { + // Redirect to login page if the user is not authenticated + return Astro.redirect('/login') + } +} +--- + +Ciao prova profilo {JSON.stringify(Astro.locals.user)}