Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9df12a8a3c | ||
|
|
c29f90ba73 | ||
|
|
59f21ebf34 | ||
|
|
9d5100e066 | ||
|
|
efd47f2a89 | ||
|
|
8a86733d5c | ||
|
|
069af4cd09 | ||
|
|
a8172f7e66 | ||
|
|
a02801c0c8 |
@@ -0,0 +1,4 @@
|
||||
Dockerfile
|
||||
node_modules
|
||||
.git
|
||||
*.local*
|
||||
@@ -7,19 +7,15 @@
|
||||
|
||||
kind: pipeline
|
||||
name: default
|
||||
type: docker
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
image: node:22-alpine
|
||||
image: node:latest
|
||||
volumes:
|
||||
- name: host-website-dist
|
||||
path: /mnt/website
|
||||
commands:
|
||||
- uname -a
|
||||
- node -v
|
||||
- npm ci
|
||||
- node -e 'import Sharp from "sharp"; console.log(Sharp)'
|
||||
- npm install
|
||||
- npm run build
|
||||
- cp -rT ./dist /mnt/website
|
||||
|
||||
@@ -36,8 +32,8 @@ trigger:
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: caddy-permissions
|
||||
type: exec # this job is executed on the host machine
|
||||
name: caddy-permissions
|
||||
|
||||
depends_on:
|
||||
- default
|
||||
|
||||
@@ -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',
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -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,50 +5,56 @@
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/node": "^9.4.3",
|
||||
"@astrojs/preact": "^4.1.1",
|
||||
"@fontsource-variable/material-symbols-outlined": "^5.2.21",
|
||||
"@fontsource/iosevka": "^5.2.5",
|
||||
"@fontsource/mononoki": "^5.2.5",
|
||||
"@fontsource/open-sans": "^5.2.6",
|
||||
"@fontsource/source-code-pro": "^5.2.6",
|
||||
"@fontsource/source-sans-pro": "^5.2.5",
|
||||
"@fontsource/space-mono": "^5.2.8",
|
||||
"@astrojs/node": "9.0.0",
|
||||
"@astrojs/preact": "4.0.0",
|
||||
"@fontsource-variable/material-symbols-outlined": "^5.1.1",
|
||||
"@fontsource/iosevka": "^5.0.11",
|
||||
"@fontsource/mononoki": "^5.0.11",
|
||||
"@fontsource/open-sans": "^5.0.24",
|
||||
"@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.10",
|
||||
"@preact/signals": "^1.3.2",
|
||||
"@phosphor-icons/react": "^2.1.7",
|
||||
"@preact/signals": "^1.3.0",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"astro": "^5.13.7",
|
||||
"fuse.js": "^7.1.0",
|
||||
"katex": "^0.16.22",
|
||||
"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.12",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"node-gyp": "^11.4.2",
|
||||
"preact": "^10.27.2",
|
||||
"sharp": "^0.34.3",
|
||||
"typescript": "^5.9.2"
|
||||
"marked": "^15.0.6",
|
||||
"preact": "^10.19.6",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/mdx": "^4.3.5",
|
||||
"@astrojs/mdx": "4.0.2",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@types/katex": "^0.16.7",
|
||||
"jsdom": "^24.1.3",
|
||||
"linkedom": "^0.18.12",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"jsdom": "^24.1.1",
|
||||
"linkedom": "^0.18.4",
|
||||
"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.92.1",
|
||||
"tsx": "^4.20.5"
|
||||
"tsx": "^4.7.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 877 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@@ -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!,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
@@ -41,13 +41,7 @@ export const ComboBox = ({
|
||||
<PhosphorIcon name="caret-down" />
|
||||
</div>
|
||||
{open && (
|
||||
<div
|
||||
class={clsx('dropdown', cloak && 'invisible')}
|
||||
ref={el => {
|
||||
if (!el) return
|
||||
setItemWidth(el.offsetWidth)
|
||||
}}
|
||||
>
|
||||
<div class={clsx('dropdown', cloak && 'invisible')} ref={el => el && setItemWidth(el.offsetWidth)}>
|
||||
{Object.keys(children).map(key => (
|
||||
<div
|
||||
class="option"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
import { FunnelIcon } from '@phosphor-icons/react'
|
||||
import { Funnel } from '@phosphor-icons/react'
|
||||
import { marked } from 'marked'
|
||||
|
||||
import extendedLatex from '@/client/lib/marked-latex'
|
||||
@@ -70,7 +70,7 @@ export const DomandeEsamiCourse = ({ course }: Props) => {
|
||||
{courseTags.length > 1 && (
|
||||
<div class="card filter">
|
||||
<div class="grid-h">
|
||||
<FunnelIcon />
|
||||
<Funnel />
|
||||
<strong>Filtra Tag</strong>
|
||||
</div>
|
||||
<div class="flex-row-wrap">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'preact/hooks'
|
||||
import { render } from 'preact'
|
||||
|
||||
// Tipi per la gestione dei dati
|
||||
type TipoStudente = 'triennale' | 'magistrale'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createAuthClient } from 'better-auth/client'
|
||||
import { genericOAuthClient } from 'better-auth/client/plugins'
|
||||
export const authClient = createAuthClient({
|
||||
plugins: [genericOAuthClient()],
|
||||
})
|
||||
@@ -9,7 +9,7 @@ const extBlock = options => ({
|
||||
start(src) {
|
||||
return src.match(/\$\$[^\$]/)?.index ?? -1
|
||||
},
|
||||
tokenizer(src, _tokens) {
|
||||
tokenizer(src, tokens) {
|
||||
const match = /^\$\$([^\$]+)\$\$/.exec(src)
|
||||
return match ? { type: 'latex-block', raw: match[0], formula: match[1] } : undefined
|
||||
},
|
||||
@@ -25,7 +25,7 @@ const extInline = options => ({
|
||||
start(src) {
|
||||
return src.match(/\$[^\$]/)?.index ?? -1
|
||||
},
|
||||
tokenizer(src, _tokens) {
|
||||
tokenizer(src, tokens) {
|
||||
const match = /^\$([^\$]+)\$/.exec(src)
|
||||
return match ? { type: 'latex', raw: match[0], formula: match[1] } : undefined
|
||||
},
|
||||
|
||||
@@ -32,17 +32,17 @@ const guidesCollection = defineCollection({
|
||||
})
|
||||
|
||||
// Per ora sono su un sito a parte ma prima o poi verranno migrati qui
|
||||
// const seminariettiCollection = defineCollection({
|
||||
// type: 'content',
|
||||
// schema: z.object({
|
||||
// title: z.string(),
|
||||
// description: z.string(),
|
||||
// author: z.string(),
|
||||
// publishDate: z.date(),
|
||||
// eventDate: z.date(),
|
||||
// tags: z.array(z.string()),
|
||||
// }),
|
||||
// })
|
||||
const seminariettiCollection = defineCollection({
|
||||
type: 'content',
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
author: z.string(),
|
||||
publishDate: z.date(),
|
||||
eventDate: z.date(),
|
||||
tags: z.array(z.string()),
|
||||
}),
|
||||
})
|
||||
|
||||
const metaCollection = defineCollection({
|
||||
type: 'content',
|
||||
@@ -53,6 +53,6 @@ const metaCollection = defineCollection({
|
||||
export const collections = {
|
||||
news: newsCollection,
|
||||
guides: guidesCollection,
|
||||
// seminarietti: seminariettiCollection,
|
||||
seminarietti: seminariettiCollection,
|
||||
meta: metaCollection,
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
id: stampare-via-ssh
|
||||
title: Stampare via SSH
|
||||
description: Istruzioni per stampare in dipartimento da remoto, tramite SSH 🖨
|
||||
author: Antonio De Lucreziis, Francesco Minnocci
|
||||
tags: [linux, ssh, stampanti]
|
||||
---
|
||||
|
||||
Per stampare in dipartimento non bisogna per forza usare i computer dei laboratori, possiamo che stampare direttamente da remoto tramite SSH. Vediamo come fare!
|
||||
|
||||
Se non l'avete mai fatto per prima cosa bisogna poter accedere da remoto ad una macchina chiamata "login", il cui indirizzo è `login.dm.unipi.it`. Per fare l'accesso possiamo usare il seguente comando con l'account di Ateneo (non quello Poisson!)
|
||||
|
||||
```bash shell
|
||||
ssh USERNAME_ATENEO@login.dm.unipi.it
|
||||
```
|
||||
|
||||
Una volta connessi possiamo stampare utilizzando il comando `lpr` seguito dal nome del file che vogliamo stampare. Prima però serve trasferire il file che vogliamo stampare sulla macchina "login". Per fare ciò possiamo usare il comando `scp`: per prima cosa usciamo dalla macchina "login" (premere `Ctrl+D` oppure scrivendo `exit`), andiamo nella cartella dove si trova il file che vogliamo stampare e poi eseguiamo il comando:
|
||||
|
||||
```bash shell
|
||||
scp NOME_FILE.pdf USERNAME_ATENEO@login.dm.unipi.it:~/Documents
|
||||
```
|
||||
|
||||
Dove `NOME_FILE.pdf` è il nome del file che vogliamo stampare e `Documents` è un esempio di cartella dove vogliamo trasferirlo. Una volta trasferito il file possiamo rifare ssh su "login" e stampare il file con il comando:
|
||||
|
||||
```bash shell
|
||||
lpr Documents/NOME_FILE.pdf
|
||||
```
|
||||
|
||||
Alternativamente possiamo stampare direttamente il file senza trasferirlo con il comando:
|
||||
|
||||
```bash shell
|
||||
cat NOME_FILE.pdf | ssh USERNAME_ATENEO@login.dm.unipi.it lpr OPZIONI... -
|
||||
```
|
||||
|
||||
Qui, `[OPZIONI...]` sono le opzioni che possiamo passare a `lpr` (vedi sotto). L'ultimo trattino "`-`" è molto importante e indica che il file da stampare è quello in standard input. Più precisamente, `cat NOME_FILE.pdf` invia il contenuto del file `NOME_FILE.pdf` allo standard output e `|` lo ridireziona a input di `ssh`, che a sua volta lo passa a `lpr` via rete.
|
||||
|
||||
## Opzioni di `lpr`
|
||||
|
||||
Il comando `lpr` accetta alcune opzioni che possono essere utili:
|
||||
|
||||
- `-P` seguito dal nome della stampante: permette di specificare la stampante su cui stampare, le stampanti disponibili in dipartimento sono
|
||||
- `cdc4` che è la stampante di default e si trova in Aula 4
|
||||
|
||||
- `cdclf` che si trova al piano terra nel corridoio dopo l'Aula 4
|
||||
|
||||
- `cdc3` che si trova in Aula 3 (è un po' vecchia ma di solito funziona)
|
||||
|
||||
- `-#` seguito dal numero di copie: permette di specificare il numero di copie da stampare. In realtà questa opzione non funziona per vari motivi arcani e se uno passa `-#N` per stampare $N$ copie, la stampante stampa $N^2$ copie. (Questo ha scaturito una serie di ragionamenti sul modo ottimo di decomporre $N$ come somma di quadrati [con tanto di sito di comodo](https://shortest-sum-of-squares.netlify.app/)...)
|
||||
|
||||
- `-o sides=two-sided-long-edge`: permette di stampare **fronte-retro** (che dovrebbe essere già il default)
|
||||
|
||||
- `-o sides=two-sided-short-edge`: permette di stampare fronte-retro con "la rilegatura" delle pagine sul lato corto
|
||||
|
||||
- `-o sides=one-sided`: permette di stampare _solo fronte_, comodo per stampare i meme di laurea
|
||||
|
||||
- `-o fit-to-page`: permette di ridimensionare il documento per farlo entrare in un foglio (è buona prassi passare sempre questa opzione)
|
||||
|
||||
- `-o media=a4`: permette di specificare il formato del foglio, di default è A4 quindi non dovrebbere servire
|
||||
|
||||
## Altre comodità
|
||||
|
||||
Stampare da remoto porta anche altre comodità, ad esempio possiamo interrompere un file che abbiamo mandato in stampa per sbaglio con il comando (sempre tutti comandi da eseguire su "login")
|
||||
|
||||
```bash shell
|
||||
cancel -a
|
||||
```
|
||||
|
||||
> Attenzione, il comando sopra cancella tutta la propria coda di stampa, non solo l'ultimo lavoro inviato.
|
||||
|
||||
Alternativamente possiamo vedere lo stato della coda di stampa con il comando
|
||||
|
||||
```bash shell
|
||||
lpq -a
|
||||
```
|
||||
|
||||
e cancellare un lavoro con uno specifico ID con
|
||||
|
||||
```bash shell
|
||||
cancel ID
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Calcola la tua media ed il voto di laurea con il nuovissimo calcolatore del PHC!
|
||||
description: È ora disponibile uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole del dipartimento.
|
||||
description: È ora disponibile uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole del dipartimento
|
||||
publishDate: 2025-06-26
|
||||
---
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
title: Esplora i meme dell'aula studenti online!
|
||||
description: |
|
||||
Gli storici meme sono stati staccati per i lavori, ma non disperare: li potrai vedere su una nuova pagina.
|
||||
publishDate: 2025-06-26
|
||||
---
|
||||
|
||||
# Esplora i meme dell'aula studenti online!
|
||||
|
||||
Visti gli imminenti lavori che occuperanno l'aula studenti, ad inizio Settembre tutti i meme sulle pareti sono stati staccati e riposti temporaneamente in PHC; qui sotto trovate alcuni timelapse della giornata:
|
||||
|
||||
<div class="grid-h-split">
|
||||
<video controls>
|
||||
<source src="https://static.phc.dm.unipi.it/timelapse-nord.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<video controls>
|
||||
<source src="https://static.phc.dm.unipi.it/timelapse-sud.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<video controls>
|
||||
<source src="https://static.phc.dm.unipi.it/timelapse-termosifone.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
Per questo motivo, li abbiamo scansionati ed abbiamo creato una bacheca per poter contemplare i meme dovunque voi siate 🧳
|
||||
|
||||
Eccovi dunque il link alla pagina, buon divertimento:
|
||||
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://meme.phc.dm.unipi.it">meme.phc.dm.unipi.it</a>
|
||||
</p>
|
||||
|
||||
## Coming Soon
|
||||
|
||||
Prima o poi faremo anche una mappa interattiva della stanza, basata sul seguente modello 3D ricostruito con tecniche di fotogrammetria:
|
||||
|
||||
<video controls>
|
||||
<source src="https://static.phc.dm.unipi.it/3d-scan-preview.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
> Disclaimer: Se volessi rimuovere una tua immagine da questa pagina, scrivici pure a <a href="mailto:macchinisti@lists.dm.unipi.it">macchinisti@lists.dm.unipi.it</a> e ce ne occuperemo.
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
title: Non avete attivato la 2FA entro il primo dicembre? Ecco come recuperare l'accesso
|
||||
description: |
|
||||
Se non avete attivato l'autenticazione a due fattori entro il primo dicembre, siete bloccati fuori dall'account Unipi. La procedura di recupero passa dal supporto tecnico.
|
||||
publishDate: 2025-12-05
|
||||
---
|
||||
|
||||
# Non avete attivato la 2FA entro il primo dicembre? Ecco come recuperare l'accesso
|
||||
|
||||
Se siamo rimasti fuori dal nostro account Unipi perché non abbiamo attivato l'autenticazione a due fattori entro il primo dicembre, il percorso di recupero è piuttosto lineare.
|
||||
|
||||
Il primo passo è mandare una mail a
|
||||
|
||||
> <a href="mailto:help.polo2@ticket.unipi.it">help.polo2@ticket.unipi.it</a>
|
||||
|
||||
usando un indirizzo email personale (quello di ateneo è bloccato). Nel messaggio dobbiamo specificare chiaramente il problema e includere il nostro indirizzo email d'ateneo.
|
||||
|
||||
Dopo aver inviato la richiesta, tocca aspettare. Il supporto tecnico resetta manualmente lo status 2FA e ci manda una conferma. A quel punto possiamo procedere con l'attivazione seguendo le istruzioni ufficiali:
|
||||
|
||||
> https://it.unipi.it/configurazioni/mfa/autenticazione-a-piu-fattori-mfa-microsoft-365/
|
||||
|
||||
**Nota tecnica:** L'app Microsoft Authenticator non è obbligatoria! Qualsiasi autenticatore compatibile con [TOTP](https://en.wikipedia.org/wiki/Time-based_one-time_password) va bene: Google Authenticator, Bitwarden, Authy e altre alternative funzionano perfettamente. L'unica differenza è che l'app Microsoft Authenticator permette di ricevere notifiche l'accesso che consente di fare login con meno click.
|
||||
|
||||
Per utilizzare una di queste altre app (invece di Microsoft Authenticator), seguire questi step nella creazione del metodo di autenticazione:
|
||||
|
||||
1. Selezionare **"Microsoft Authenticator"**
|
||||
|
||||

|
||||
|
||||
2. Selezionare **"Configura un'app di autenticazione diversa"**
|
||||
|
||||

|
||||
|
||||
3. A questo punto il procedimento cambia in base all'app. In generale verrà richiesto di scannerizzare un QR code e di verificare il corretto funzionamento inserendo la TOTP (codice a 6 cifre)
|
||||
@@ -749,7 +749,7 @@ questions:
|
||||
- course: aritmetica
|
||||
content: |
|
||||
Quanti sono i polinomi irriducibili di grado $n$ su $\mathbb{F}_p$? (Hint: può essere utile provare prima il caso dei polinomi di secondo grado. Hint: in alternativa si possono contare i polinomi riducibili.)
|
||||
Hint: Altrimenti dimostrate che
|
||||
Hint: Altrimenti dimostrate che
|
||||
$$\prod_{\mathclap{\substack{p(x) \text{irriducibile} \\ \deg(p(x)) | n}}} p(x) = x^{p^n} - x.$$
|
||||
tags:
|
||||
- 2015
|
||||
@@ -1061,7 +1061,7 @@ questions:
|
||||
|
||||
- course: ricerca-operativa
|
||||
content: |
|
||||
Dato il problema $\max (x_1 + 2x_2)$ soggetto ai vincoli:
|
||||
Dato il problema $\max (x_1 + 2x_2)$ soggetto ai vincoli:
|
||||
$$
|
||||
\begin{cases}
|
||||
x_2 \leq 4 \\
|
||||
@@ -1117,400 +1117,6 @@ questions:
|
||||
tags:
|
||||
- 2023
|
||||
|
||||
# Raccolta di Istituzioni di Geometria da Valeria
|
||||
|
||||
# Raccolta di Istituzioni di Geometria da Fra
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Intorno tubolare, esistenza
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Gruppi di Lie, esiste un unico sottogruppo connesso con data sottoalgebra
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Frobenius
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Invarianza omotopica di de Rham
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Mayer-Vietoris
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Cartan-Hadamard
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Lemma di Gauss
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Whitney
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Localmente euclideo se e solo se R=0
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Mayer-Vietoris a supporto compatto
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Orientabilità di RP^n e CP^n
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Lemma di Poincaré (dimostrare anche che mappe omotope inducono stesso pullback sulla coomologia), brevissimo accenno al caso a supporto compatto (nessuna dimostrazione)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Cos'è il trasporto parallelo (tutti i dettagli di buona definizione a partire dalla definizione di campo parallelo)? Se metto una metrica cosa succede? (Statement delle condizioni equivalenti di compatibilità senza dimostrazione)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Dimostra che l'algebra di Lie associata ad un gruppo di Lie è un'algebra di Lie in senso astratto (voleva giusto sentirsi dire che il bracket di campi invarianti a sinistra è invariante a sinistra senza dimostrazione)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema di corrispondenza algebre di lie
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Quando gli spazi proiettivi reali sono orientabili
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Esistenza dell'intorno tubolare (senza dimostrazione degli "esercizi")
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Dualità di Poincaré 2
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definizione di connessione metrica e condizioni equivalenti (solo enunciato)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Hopf-Rinow
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Isotopia Ambiente
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Differenziale Esterno
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Stokes
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Raddrizzamento simultaneo
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definizione di fibrato tangente
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definizione di intorno tubolare e esistenza
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema di Frobenius
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Piatto se e solo se localmente isometrico
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Quando lo spazio proiettivo reale è orientabile? E quello complesso?
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Isotopia e isotopia ambiente
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Differenziale k-forme
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Corrispondenza tra sottogruppi di lie connessi e sottoalgebre di lie
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Torsione di una connessione
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Dualità di poincarè (perché la mappa DP passa in coomologia?)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Parlare dell'algebra di Lie associata a un Gruppo di Lie
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Enunciato di Frobenius, e perché una distribuzione è integrabile sse è loc. costante
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Poincaré Dualità
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Conseguenze (Betti numeri, specchiabilità)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Distribuzioni, esempi di distribuzioni che (non) sono embeddings
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Trasporto parallelo e geodetiche
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Esempio di varietà non geodeticamente completa, si può avere compatto? (Hopf rinow)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Esistenza di forma volume per varietà orientate e di struttura riemanniana sui fibrati
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definizione di foliazione
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Localmente euclidea sse piatto
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema di Stokes
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema di Hopf-Rinow
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Orientabilità di CP^n
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definizione di forma volume ed esistenza
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Stokes (dim.)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
isotopia e isotopia ambiente (def. + teo.)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
tensore Riemann (def.)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
teo. sottoalgebre di Lie
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
esistenza metrica Lorentziana e Riemanniana
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
derivata di k-forme
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
lemma Poincaré e teo. sull'omotopia
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Cos'è l'algebra di Lie (molto veloce)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Cos'è la connessione di Levi Civita ed elencare le forme equivalenti di "compatibilità con la metrica"
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Definire la torsione e fare i conti per mostrare che è un tensore
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema di Whitney (caso compatto e caso generale).
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
M ammette struttura lorentziana orientabile temporalmente sse pettinabile.
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Teorema su sottoalgebre di Lie
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
geodetiche (praticamente solo quale è l'equazione e perché ha senso fare la nabla di un campo su una curva)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Frobenius, enunciato e dimostrazione (dando per scontate le def.equivalenti di foliazione)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
Whitney (entrambi)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
content: |
|
||||
quando il proiettivo è orientabile (ed enunciato del teorema da cui segue)
|
||||
tags:
|
||||
- 2025
|
||||
|
||||
# Raccolta di Istituzioni di Geometria da Fra
|
||||
|
||||
- course: istituzioni-di-geometria
|
||||
@@ -2525,25 +2131,25 @@ questions:
|
||||
- course: geometria-e-topologia-differenziale
|
||||
content: |
|
||||
Che cos'è un'orientazione su una varietà?
|
||||
Se la varietà è connessa e orientabile, mostrare che esistono esattamente 2 orientazioni
|
||||
Se la varietà è connessa e orientabile, mostrare che esistono esattamente 2 orientazioni
|
||||
(se non connessa, ce n'è una per ogni componente)
|
||||
tags:
|
||||
- 2023
|
||||
|
||||
- course: geometria-e-topologia-differenziale
|
||||
content: |
|
||||
Per quale motivo è lecito parlare di orientazione "naturale"
|
||||
Per quale motivo è lecito parlare di orientazione "naturale"
|
||||
(nel senso di cosa è permesso e cosa no)?
|
||||
tags:
|
||||
- 2023
|
||||
|
||||
- course: geometria-e-topologia-differenziale
|
||||
content: |
|
||||
Per un diffeomorfismo da una varietà connessa su se stessa,
|
||||
o ne rispetta l'orientazione, o la "flippa".
|
||||
Mostrare (idee) che esiste tale $f$ che "flippa"
|
||||
Per un diffeomorfismo da una varietà connessa su se stessa,
|
||||
o ne rispetta l'orientazione, o la "flippa".
|
||||
Mostrare (idee) che esiste tale $f$ che "flippa"
|
||||
(ad esempio considerare un $f_0 : x \mapsto -x$ che flippa la sfera in $\mathbb{R}^n$
|
||||
e analogamente si può fare per altre varietà in punto,
|
||||
e analogamente si può fare per altre varietà in punto,
|
||||
allora viene flippata sempre)
|
||||
tags:
|
||||
- 2023
|
||||
|
||||
@@ -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"]
|
||||
})
|
||||
@@ -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>
|
||||
@@ -11,11 +11,7 @@ import Card from '@/components/Card.astro'
|
||||
|
||||
const news = await getCollection('news')
|
||||
|
||||
// const galleryCollage = await Astro.glob('@/assets/gallery/*.jpg')
|
||||
|
||||
const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
import.meta.glob('@/assets/gallery/*.jpg', { eager: true }),
|
||||
)
|
||||
const galleryCollage = await Astro.glob('@/assets/gallery/*.jpg')
|
||||
---
|
||||
|
||||
<PageLayout title="PHC" pageTags="homepage">
|
||||
@@ -51,23 +47,21 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
|
||||
<div class="card-list">
|
||||
{
|
||||
news
|
||||
.sort((s, t) => -s.id.localeCompare(t.id))
|
||||
.map(newsItem => (
|
||||
<Card>
|
||||
<a href={`/notizie/${newsItem.slug}`} class="title">
|
||||
{newsItem.data.title}
|
||||
</a>
|
||||
<div class="text small dimmed">
|
||||
{new Date(newsItem.data.publishDate).toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<div class="text">{newsItem.data.description}</div>
|
||||
</Card>
|
||||
))
|
||||
news.toReversed().map(newsItem => (
|
||||
<Card>
|
||||
<a href={`/notizie/${newsItem.slug}`} class="title">
|
||||
{newsItem.data.title}
|
||||
</a>
|
||||
<div class="text small dimmed">
|
||||
{new Date(newsItem.data.publishDate).toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<div class="text">{newsItem.data.description}</div>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -100,9 +94,9 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
</ProjectCard> -->
|
||||
<ProjectCard
|
||||
title="Orario"
|
||||
href="https://orario.phc.dm.unipi.it/"
|
||||
href="https://lab.phc.dm.unipi.it/orario/"
|
||||
style="--card-bg: #75ca75; --masonry-height: 1;"
|
||||
imgSrc="https://orario.phc.dm.unipi.it/icon.png"
|
||||
imgSrc="https://lab.phc.dm.unipi.it/orario/icon.png"
|
||||
>
|
||||
<p>
|
||||
Questo sito permette di visualizzare il proprio orario delle lezioni, con informazioni sui docenti e
|
||||
@@ -111,9 +105,9 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
</ProjectCard>
|
||||
<ProjectCard
|
||||
title="Problemi"
|
||||
href="https://problemi.phc.dm.unipi.it/"
|
||||
href="https://lab.phc.dm.unipi.it/problemi/"
|
||||
style="--card-bg: #aa88c0; --masonry-height: 2;"
|
||||
imgSrc="https://problemi.phc.dm.unipi.it/favicon/android-chrome-512x512.png"
|
||||
imgSrc="https://lab.phc.dm.unipi.it/problemi/favicon/android-chrome-512x512.png"
|
||||
>
|
||||
<p>Bacheca di problemi di Matematica da risolvere, inviandone le soluzioni in LaTeX.</p>
|
||||
<p>Di sera, puoi leggere i problemi sullo schermo del Dipartimento in atrio.</p>
|
||||
@@ -122,7 +116,7 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
title='Cluster "Steffè"'
|
||||
href="http://steffe.cs.dm.unipi.it/"
|
||||
style="--masonry-height: 2;"
|
||||
imgSrc="/images/steffe-logo.png"
|
||||
imgSrc="https://steffe.lb.cs.dm.unipi.it/assets/img/logo.png"
|
||||
>
|
||||
<p>
|
||||
Cluster progettato ed assemblato durante il progetto speciale per la didattica "Calcolo Parallelo
|
||||
@@ -152,7 +146,7 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
title="GGWP"
|
||||
href="https://ggwp.phc.dm.unipi.it/"
|
||||
style="--card-bg: rgb(255, 157, 65); --masonry-height: 2;"
|
||||
imgSrc="/images/ggwp-icon.png"
|
||||
imgSrc="https://ggwp.phc.dm.unipi.it/ggwp-icon.png"
|
||||
>
|
||||
<p>
|
||||
Sito per il tabellone del GGWP, utilizzato durante la gara di Novembre 2024 dagli organizzatori per
|
||||
@@ -163,7 +157,7 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
title="Chat"
|
||||
href="https://chat.phc.dm.unipi.it/"
|
||||
style="--card-bg: #383838; --card-fg: #ddd; --masonry-height: 1;"
|
||||
imgSrc="/images/chat-favicon.png"
|
||||
imgSrc="https://chat.phc.dm.unipi.it/favicon.png"
|
||||
>
|
||||
<p>Istanza di Open-WebUI, una chat stile ChatGPT con alcuni LLM self-hostati dal PHC.</p>
|
||||
</ProjectCard>
|
||||
@@ -204,16 +198,14 @@ const galleryCollage: { default: ImageMetadata }[] = Object.values(
|
||||
{
|
||||
galleryCollage.map((module, i) => {
|
||||
const src = module.default.src
|
||||
const filename = src.split('/').at(-1)?.split('?').at(0)?.split('.').at(0)
|
||||
|
||||
if (!filename) return null
|
||||
const filename = src.split('/').at(-1).split('?').at(0).split('.').at(0)
|
||||
|
||||
const [rows, cols] = filename.includes('@')
|
||||
? (filename
|
||||
? filename
|
||||
.split('@')
|
||||
.at(-1)
|
||||
?.split('x')
|
||||
.map((s: string) => parseInt(s)) ?? [1, 1])
|
||||
.split('x')
|
||||
.map((s: string) => parseInt(s))
|
||||
: [1, 1]
|
||||
|
||||
return (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -12,23 +12,21 @@ const news = await getCollection('news')
|
||||
<h1><a href="/notizie">Notizie</a></h1>
|
||||
<div class="card-list">
|
||||
{
|
||||
news
|
||||
.sort((s, t) => -s.id.localeCompare(t.id))
|
||||
.map(newsItem => (
|
||||
<div class="card">
|
||||
<a href={`/notizie/${newsItem.slug}`} class="title">
|
||||
{newsItem.data.title}
|
||||
</a>
|
||||
<div class="text small dimmed">
|
||||
{new Date(newsItem.data.publishDate).toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<div class="text">{newsItem.data.description}</div>
|
||||
news.toReversed().map(newsItem => (
|
||||
<div class="card">
|
||||
<a href={`/notizie/${newsItem.slug}`} class="title">
|
||||
{newsItem.data.title}
|
||||
</a>
|
||||
<div class="text small dimmed">
|
||||
{new Date(newsItem.data.publishDate).toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
))
|
||||
<div class="text">{newsItem.data.description}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</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;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1042,20 +1042,6 @@ $news-accent-bg: #f8e8b1;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
|
||||
.grid-h-split {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
gap: 1rem;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 1fr;
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
grid-auto-flow: row;
|
||||
grid-auto-columns: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
min-height: calc(100vh - 10rem);
|
||||
}
|
||||
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; */
|
||||
|
||||
& {
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
@@ -164,10 +168,6 @@
|
||||
color: #000d;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
section.projects {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +146,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -157,7 +156,7 @@
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0.25rem 0.25rem 0 0 #333;
|
||||
background: #000;
|
||||
/* background: color-mix(in lab, var(--card-base-internal, #ededed), #fff 20%); */
|
||||
|
||||
&.fill {
|
||||
width: 100%;
|
||||
@@ -174,10 +173,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
video {
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||