Compare commits

...

9 Commits

@ -0,0 +1,4 @@
Dockerfile
node_modules
.git
*.local*

@ -0,0 +1,11 @@
DB_FILE_NAME=file:data.local/database.db
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000
OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET=
OAUTH_AUTH_URL=https://iam.unipi.it/oauth2/authorize
OAUTH_TOKEN_HOST=https://iam.unipi.it
OAUTH_TOKEN_PATH=/oauth2/token
OAUTH_REDIRECT_URL=https://phc.dm.unipi.it/api/auth/oauth2/callback/unipi
OAUTH_USER_INFO_URL=https://iam.unipi.it/oauth2/userinfo
OAUTH_SCOPES="openid profile email"

@ -0,0 +1,35 @@
# https://dev.to/code42cate/how-to-dockerize-and-deploy-astro-6ll
# use the official Bun image
# see all versions at https://hub.docker.com/r/oven/bun/tags
FROM oven/bun:1-alpine AS base
WORKDIR /app
RUN apk add --no-cache \
git \
curl \
build-base \
python3
# install dependencies into temp directory to cache them and speed up future builds
FROM base AS install
RUN mkdir -p /temp/dev
COPY package.json bun.lockb /temp/dev/
RUN cd /temp/dev && bun install --frozen-lockfile
RUN mkdir -p /temp/prod
COPY package.json bun.lockb /temp/prod/
RUN cd /temp/prod && bun install --frozen-lockfile --production
# copy node_modules from temp directory and copy (non-ignored) project files into the image
FROM base AS prerelease
COPY --from=install /temp/dev/node_modules node_modules
COPY . .
RUN bun run build
# copy production dependencies and source code into final image
FROM base AS release
COPY --from=install /temp/prod/node_modules node_modules
COPY --from=prerelease /app .
ENV HOST=0.0.0.0
ENV PORT=3000
EXPOSE 3000/tcp
CMD bun run start

@ -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',
}),
})

Binary file not shown.

@ -0,0 +1,12 @@
services:
website:
build:
context: .
container_name: website
restart: unless-stopped
ports:
# https://gist.github.com/aziis98/88af12b32d9cf3eeae3929b93146fd27
# hash2addr "next.phc.dm.unipi.it"
- "127.44.207.62:1059:3000"
volumes:
- /var/lib/website:/app/data.local

@ -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!,
},
})

@ -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
);

@ -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": {}
}
}

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1749233567397,
"tag": "0000_violet_agent_zero",
"breakpoints": true
}
]
}

@ -5,6 +5,10 @@
"scripts": {
"dev": "run-s astro:sync astro:dev",
"build": "run-s astro:build",
"astro:server": "bun run ./dist/server/entry.mjs",
"drizzle:migrate": "drizzle-kit migrate",
"drizzle:generate": "drizzle-kit generate",
"start": "run-s drizzle:migrate astro:server",
"astro:sync": "astro sync",
"astro:dev": "astro dev",
"astro:build": "astro check && astro build"
@ -20,16 +24,23 @@
"@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",
"drizzle-kit": "^0.31.1",
"fuse.js": "^7.0.0",
"katex": "^0.16.9",
"lucide-static": "^0.468.0",
"marked": "^15.0.6",
"preact": "^10.19.6",
"npm-run-all": "^4.1.5",
"typescript": "^5.3.3"
},
"devDependencies": {
@ -38,14 +49,12 @@
"@types/katex": "^0.16.7",
"jsdom": "^24.1.1",
"linkedom": "^0.18.4",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.0",
"prettier-plugin-astro": "^0.14.1",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"remark-math": "^6.0.0",
"remark-toc": "^9.0.0",
"sass": "^1.71.1",
"tsx": "^4.7.1"
}
}

@ -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!,
},
],
}),
],
})

@ -0,0 +1,5 @@
import { createAuthClient } from 'better-auth/client'
import { genericOAuthClient } from 'better-auth/client/plugins'
export const authClient = createAuthClient({
plugins: [genericOAuthClient()],
})

@ -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()),
})

@ -0,0 +1,4 @@
import 'dotenv/config'
import { drizzle } from 'drizzle-orm/libsql'
export default drizzle(process.env.DB_FILE_NAME!)

@ -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"]
})

8
src/env.d.ts vendored

@ -1,2 +1,10 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
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
}
}

@ -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()
})

@ -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)
}

@ -0,0 +1,8 @@
<script>
import { authClient } from '@/client/auth-client' //import the auth client
await authClient.signIn.oauth2({
providerId: 'unipi',
callbackURL: '/profilo', // the path to redirect to after the user is authenticated
})
</script>

@ -1,5 +1,6 @@
---
import PageLayout from '../layouts/PageLayout.astro'
import '@/styles/pages/login.css'
---
<PageLayout title="Login | PHC" pageTags="login">
@ -7,16 +8,25 @@ import PageLayout from '../layouts/PageLayout.astro'
<!-- form with username and password, and a button for oauth login -->
<form action="/login" method="post">
<h3 class="center">Accedi con Poisson</h3>
<input type="text" id="username" placeholder="Username" name="username" required />
<input type="password" id="password" placeholder="Password" name="password" required />
<h3 class="center"><strike>Accedi con Poisson</strike> (Work in Progress)</h3>
<button class="primary center" type="submit">Login</button>
<div class="row">
<span class="material-symbols-outlined">person</span>
<input type="text" id="username" placeholder="Username" name="username" required disabled />
</div>
<div class="row">
<span class="material-symbols-outlined">key</span>
<input type="password" id="password" placeholder="Password" name="password" required disabled />
</div>
<button class="primary center" type="submit" disabled>Login</button>
<hr />
<h3 class="center">Accedi con Ateneo</h3>
<a href="/auth/ateneo" class="primary center" role="button">Login</a>
<a href="/auth/ateneo" class="primary center" role="button">
<span class="material-symbols-outlined">account_balance</span>
Login
</a>
</form>
<!-- <span class="material-symbols-outlined">person</span> -->
</PageLayout>

@ -0,0 +1,40 @@
---
import PageLayout from '../layouts/PageLayout.astro'
import '@/styles/pages/login.css'
if (!Astro.locals.user) {
return Astro.redirect('/login')
}
const { name, email } = Astro.locals.user
---
<PageLayout title="Profilo | PHC">
<div class="card large">
<div class="title">Profilo di {name}</div>
<div class="text">
<p>
<strong>Nome:</strong>
{name}
<br />
<strong>Email:</strong>
{email}
<br />
</p>
<p>
Benvenuto nel tuo profilo! Qui puoi visualizzare le tue informazioni personali e modificare le tue
impostazioni.
</p>
</div>
</div>
<div class="card">
<div class="title">Impostazioni</div>
<div class="text">
<p>
Al momento non sono disponibili impostazioni modificabili. Se hai bisogno di assistenza, contatta il
supporto tecnico.
</p>
</div>
</div>
</PageLayout>

@ -1,27 +0,0 @@
/* This file is here for historical reasons but is not used anymore */
@layer page {
/*
.login {
background: #ddfaff;
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
max-width: 80ch;
padding: 3rem 0;
gap: 3rem;
h3 {
font-size: 28px;
font-weight: 600;
}
}
}
*/
}

@ -21,6 +21,8 @@ Controls - for things like buttons, input, select
display: grid;
place-content: center;
grid-auto-flow: column;
gap: 0.25rem;
&:hover {
transform: translate(-1px, -1px);
@ -206,6 +208,23 @@ Controls - for things like buttons, input, select
background: #4ea2b1;
}
}
&:disabled {
background: #aaa;
color: #666;
cursor: not-allowed;
&:active {
transform: none;
box-shadow: 4px 4px 0 0 #222;
}
&:hover {
background: #aaa;
transform: none;
box-shadow: 4px 4px 0 0 #222;
}
}
}
hr {
@ -217,6 +236,25 @@ Controls - for things like buttons, input, select
margin-top: 1rem;
}
> .row {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: 0.5rem;
span.material-symbols-outlined {
font-size: 1.5rem;
color: #222;
}
input[type='text'],
input[type='password'] {
width: 100%;
min-height: 2.5rem;
padding-left: 0.5rem;
}
}
.right {
justify-self: end;
}
@ -226,6 +264,12 @@ Controls - for things like buttons, input, select
.center {
justify-self: center;
}
@media screen and (max-width: 1024px) {
min-width: 0;
padding: 1.5rem;
gap: 1.5rem;
}
}
details {

@ -0,0 +1,17 @@
@layer page {
main {
justify-self: center;
display: grid;
place-items: center;
place-content: center;
padding: 4.5rem 3rem;
gap: 4.5rem;
@media screen and (max-width: 1024px) {
padding: 3rem 1.5rem;
gap: 3rem;
}
}
}
Loading…
Cancel
Save