Compare commits
26 Commits
@ -1,4 +0,0 @@
|
||||
Dockerfile
|
||||
node_modules
|
||||
.git
|
||||
*.local*
|
||||
@ -1,11 +0,0 @@
|
||||
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"
|
||||
@ -1,35 +0,0 @@
|
||||
# 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
|
||||
@ -1,12 +0,0 @@
|
||||
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
|
||||
@ -1,11 +0,0 @@
|
||||
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!,
|
||||
},
|
||||
})
|
||||
@ -1,82 +0,0 @@
|
||||
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
|
||||
);
|
||||
@ -1,555 +0,0 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1749233567397,
|
||||
"tag": "0000_violet_agent_zero",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 877 KiB |
@ -1,28 +0,0 @@
|
||||
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!,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
@ -1,5 +0,0 @@
|
||||
import { createAuthClient } from 'better-auth/client'
|
||||
import { genericOAuthClient } from 'better-auth/client/plugins'
|
||||
export const authClient = createAuthClient({
|
||||
plugins: [genericOAuthClient()],
|
||||
})
|
||||
@ -0,0 +1,80 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
@ -0,0 +1,46 @@
|
||||
---
|
||||
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.
|
||||
@ -0,0 +1,34 @@
|
||||
---
|
||||
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)
|
||||
@ -1,57 +0,0 @@
|
||||
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()),
|
||||
})
|
||||
@ -1,4 +0,0 @@
|
||||
import 'dotenv/config'
|
||||
import { drizzle } from 'drizzle-orm/libsql'
|
||||
|
||||
export default drizzle(process.env.DB_FILE_NAME!)
|
||||
@ -1,33 +0,0 @@
|
||||
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,18 +0,0 @@
|
||||
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()
|
||||
})
|
||||
@ -1,8 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
<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,40 +0,0 @@
|
||||
---
|
||||
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>
|
||||
@ -0,0 +1,27 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
@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…
Reference in New Issue