diff --git a/Makefile b/Makefile index 7859028..3e6579b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ # By: rparodi +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2023/11/12 11:05:05 by rparodi #+# #+# # -# Updated: 2025/07/28 17:49:54 by maiboyer ### ########.fr # +# Updated: 2025/08/02 20:48:04 by maiboyer ### ########.fr # # # # **************************************************************************** # @@ -119,5 +119,10 @@ npm@fclean: npm@clean npm@build: npm --prefix=./src/ run build +# this convert the .dbml file to an actual sql file that SQLite can handle :) +sql: + @echo "if the command isn't found, contact maieul :)" + dbml_sqlite -t -f -w ./src/@shared/src/database/init.sql ./src/@shared/src/database/init.dbml + # phony -.PHONY: all clean fclean re header footer npm_install npm_clear +.PHONY: all clean fclean re header footer npm@install npm@clean npm@fclean npm@build sql diff --git a/flake.nix b/flake.nix index 88dc1d4..1e5be0a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,13 +1,20 @@ { description = "Flake utils demo"; - inputs.nixpkgs.url = "github:nixos/nixpkgs"; - inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + dbmlSQLite = { + url = "github:maix0/DBML_SQLite"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; outputs = { self, nixpkgs, flake-utils, + dbmlSQLite, }: flake-utils.lib.eachDefaultSystem ( system: let @@ -21,7 +28,8 @@ nodejs_24 pnpm typescript - + dbmlSQLite.packages.${system}.default + # allow building better-sqlite3 clang ]; diff --git a/src/@shared/src/database/init.sql b/src/@shared/src/database/init.sql index 287c138..36f5257 100644 --- a/src/@shared/src/database/init.sql +++ b/src/@shared/src/database/init.sql @@ -1,9 +1,32 @@ --- this file will make sure that the database is always up to date with the correct schema --- when editing this file, make sure to always include stuff like `IF NOT EXISTS` such as to not throw error --- NEVER DROP ANYTHING IN THIS FILE -CREATE TABLE IF NOT EXISTS users ( - id STRING UNIQUE PRIMARY KEY, -- UUIDv7 as a string - name STRING UNIQUE, -- name of the user - token STRING UNIQUE, -- the token of the user (aka the cookie) +CREATE TABLE IF NOT EXISTS user ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT NOT NULL UNIQUE, + password TEXT +); +CREATE TABLE IF NOT EXISTS auth ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + provider INTEGER NOT NULL, + user INTEGER NOT NULL, + oauth2_user TEXT NOT NULL UNIQUE, + FOREIGN KEY(provider) REFERENCES provider(id), + FOREIGN KEY(user) REFERENCES user(id) +); +CREATE TABLE IF NOT EXISTS provider ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT PRIMARY KEY NOT NULL, + displayName TEXT NOT NULL, + secret TEXT NOT NULL, + token_url TEXT NOT NULL, + auth_url TEXT NOT NULL, + me_url TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS session ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + cookie TEXT PRIMARY KEY NOT NULL UNIQUE, + userid INTEGER NOT NULL, + createAt TEXT NOT NULL, + userAgent TEXT NOT NULL, + reason INTEGER, + FOREIGN KEY(userid) REFERENCES user(id), + FOREIGN KEY(reason) REFERENCES provider(id) ); - diff --git a/src/auth/.dockerignore b/src/auth/.dockerignore new file mode 100644 index 0000000..c925c21 --- /dev/null +++ b/src/auth/.dockerignore @@ -0,0 +1,2 @@ +/dist +/node_modules diff --git a/src/auth/package.json b/src/auth/package.json new file mode 100644 index 0000000..36fb649 --- /dev/null +++ b/src/auth/package.json @@ -0,0 +1,37 @@ +{ + "type": "module", + "private": false, + "name": "auth", + "version": "1.0.0", + "description": "This project was bootstrapped with Fastify-CLI.", + "main": "app.ts", + "directories": { + "test": "test" + }, + "scripts": { + "start": "npm run build && node dist/run.js", + "build": "vite build", + "build:prod": "vite build --outDir=/dist --minify=true --sourcemap=false" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@fastify/autoload": "^6.3.1", + "@fastify/formbody": "^8.0.2", + "@fastify/multipart": "^9.0.3", + "@fastify/sensible": "^6.0.0", + "@fastify/static": "^8.2.0", + "fastify": "^5.0.0", + "fastify-cli": "^7.4.0", + "fastify-plugin": "^5.0.0", + "raw-body": "^3.0.0", + "sharp": "^0.34.2" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "rollup-plugin-node-externals": "^8.0.1", + "vite": "^7.0.6", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/src/auth/src/app.ts b/src/auth/src/app.ts new file mode 100644 index 0000000..f385cde --- /dev/null +++ b/src/auth/src/app.ts @@ -0,0 +1,45 @@ +import { FastifyPluginAsync } from 'fastify' +import fastifyFormBody from '@fastify/formbody' +import fastifyMultipart from '@fastify/multipart' +import { mkdir } from 'node:fs/promises' +import fp from 'fastify-plugin' + +const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true }); +const routes = import.meta.glob('./routes/**/*.ts', { eager: true }); + + +// When using .decorate you have to specify added properties for Typescript +declare module 'fastify' { + export interface FastifyInstance { + image_store: string; + } +} + +const app: FastifyPluginAsync = async ( + fastify, + opts +): Promise => { + // Place here your custom code! + for (const plugin of Object.values(plugins)) { + void fastify.register(plugin, {}); + } + for (const route of Object.values(routes)) { + void fastify.register(route, {}); + } + + //void fastify.register(MyPlugin, {}) + void fastify.register(fastifyFormBody, {}) + void fastify.register(fastifyMultipart, {}) + + // The use of fastify-plugin is required to be able + // to export the decorators to the outer scope + void fastify.register(fp(async (fastify) => { + const image_store = process.env.USER_ICONS_STORE ?? "/tmp/icons"; + fastify.decorate('image_store', image_store) + await mkdir(fastify.image_store, { recursive: true }) + })) + +} + +export default app +export { app } diff --git a/src/auth/src/plugins/README.md b/src/auth/src/plugins/README.md new file mode 100644 index 0000000..1e61ee5 --- /dev/null +++ b/src/auth/src/plugins/README.md @@ -0,0 +1,16 @@ +# Plugins Folder + +Plugins define behavior that is common to all the routes in your +application. Authentication, caching, templates, and all the other cross +cutting concerns should be handled by plugins placed in this folder. + +Files in this folder are typically defined through the +[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module, +making them non-encapsulated. They can define decorators and set hooks +that will then be used in the rest of your application. + +Check out: + +* [The hitchhiker's guide to plugins](https://fastify.dev/docs/latest/Guides/Plugins-Guide/) +* [Fastify decorators](https://fastify.dev/docs/latest/Reference/Decorators/). +* [Fastify lifecycle](https://fastify.dev/docs/latest/Reference/Lifecycle/). diff --git a/src/auth/src/plugins/sensible.ts b/src/auth/src/plugins/sensible.ts new file mode 100644 index 0000000..5be2c8a --- /dev/null +++ b/src/auth/src/plugins/sensible.ts @@ -0,0 +1,11 @@ +import fp from 'fastify-plugin' +import sensible, { FastifySensibleOptions } from '@fastify/sensible' + +/** + * This plugins adds some utilities to handle http errors + * + * @see https://github.com/fastify/fastify-sensible + */ +export default fp(async (fastify) => { + fastify.register(sensible) +}) diff --git a/src/auth/src/routes/set.ts b/src/auth/src/routes/set.ts new file mode 100644 index 0000000..1d315a6 --- /dev/null +++ b/src/auth/src/routes/set.ts @@ -0,0 +1,49 @@ +import { FastifyPluginAsync } from 'fastify' +import { join } from 'node:path' +import { open } from 'node:fs/promises' +import sharp from 'sharp' +import { newUUIDv7 } from '@shared/uuid' +import rawBody from 'raw-body' + +const route: FastifyPluginAsync = async (fastify, opts): Promise => { + // await fastify.register(authMethod, {}); + // here we register plugins that will be active for the current fastify instance (aka everything in this function) + + // we register a route handler for: `/` + // it sets some configuration options, and set the actual function that will handle the request + + fastify.addContentTypeParser('*', function(request, payload, done) { + done() + }); + + fastify.post('/:userid', async function(request, reply) { + let buffer = await rawBody(request.raw); + // this is how we get the `:userid` part of things + const userid: string | undefined = (request.params as any)['userid']; + if (userid === undefined) { + return await reply.code(403); + } + const image_store: string = fastify.getDecorator('image_store') + const image_path = join(image_store, userid) + + try { + let img = sharp(buffer); + img.resize({ + height: 128, + width: 128, + fit: 'fill', + }) + const data = await img.png({ compressionLevel: 6 }).toBuffer() + let image_file = await open(image_path, "w", 0o666) + await image_file.write(data); + await image_file.close() + } catch (e: any) { + fastify.log.error(`Error: ${e}`); + reply.code(400); + return { status: "error", message: e.toString() }; + } + }) +} + +export default route + diff --git a/src/auth/src/run.ts b/src/auth/src/run.ts new file mode 100644 index 0000000..47c6f48 --- /dev/null +++ b/src/auth/src/run.ts @@ -0,0 +1,30 @@ +// this sould only be used by the docker file ! + +import fastify, { FastifyInstance } from "fastify"; +import app from './app.js' + +const start = async () => { + const envToLogger = { + development: { + transport: { + target: 'pino-pretty', + options: { + translateTime: 'HH:MM:ss Z', + ignore: 'pid,hostname', + }, + }, + }, + production: true, + test: false, + } + + const f: FastifyInstance = fastify({ logger: envToLogger.development }); + try { + await f.register(app, {}); + await f.listen({ port: 80, host: '0.0.0.0' }) + } catch (err) { + f.log.error(err) + process.exit(1) + } +} +start() diff --git a/src/auth/tsconfig.json b/src/auth/tsconfig.json new file mode 100644 index 0000000..e6d24e2 --- /dev/null +++ b/src/auth/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": {}, + "include": ["src/**/*.ts"] +} diff --git a/src/auth/vite.config.js b/src/auth/vite.config.js new file mode 100644 index 0000000..2b9d86b --- /dev/null +++ b/src/auth/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite' +import tsconfigPaths from 'vite-tsconfig-paths' +import nodeExternals from 'rollup-plugin-node-externals' +import path from 'node:path' + +export default defineConfig({ + root: __dirname, // service root + plugins: [tsconfigPaths(), nodeExternals()], + build: { + + ssr: true, + outDir: 'dist', + emptyOutDir: true, + lib: { + entry: path.resolve(__dirname, 'src/run.ts'), // adjust main entry + formats: ['cjs'], // CommonJS for Node.js + fileName: () => 'index.js', + }, + rollupOptions: { + external: [], + }, + target: 'node24', // or whatever Node version you use + sourcemap: true, + minify: false, // for easier debugging + } +}) diff --git a/src/icons/test/helper.ts b/src/icons/test/helper.ts deleted file mode 100644 index c42414a..0000000 --- a/src/icons/test/helper.ts +++ /dev/null @@ -1,43 +0,0 @@ -// This file contains code that we reuse between our tests. -import helper from 'fastify-cli/helper.js' -import * as test from 'node:test' -import * as path from 'node:path' -import { fileURLToPath } from 'node:url' - -export type TestContext = { - after: typeof test.after -} - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) -const AppPath = path.join(__dirname, '..', 'src', 'app.ts') - -// Fill in this config with all the configurations -// needed for testing the application -function config () { - return { - skipOverride: true // Register our application with fastify-plugin - } -} - -// Automatically build and tear down our instance -async function build (t: TestContext) { - // you can set all the options supported by the fastify CLI command - const argv = [AppPath] - - // fastify-plugin ensures that all decorators - // are exposed for testing purposes, this is - // different from the production setup - const app = await helper.build(argv, config()) - - // Tear down our app after we are done - // eslint-disable-next-line no-void - t.after(() => void app.close()) - - return app -} - -export { - config, - build -} diff --git a/src/icons/test/plugins/support.test.ts b/src/icons/test/plugins/support.test.ts deleted file mode 100644 index b260072..0000000 --- a/src/icons/test/plugins/support.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { test } from 'node:test' -import * as assert from 'node:assert' -import Fastify from 'fastify' -import Support from '../../src/plugins/support.js' - -test('support works standalone', async (t) => { - const fastify = Fastify() - // eslint-disable-next-line no-void - void fastify.register(Support) - await fastify.ready() - - assert.equal(fastify.someSupport(), 'hugs') -}) diff --git a/src/icons/test/routes/example.test.ts b/src/icons/test/routes/example.test.ts deleted file mode 100644 index 87f26ab..0000000 --- a/src/icons/test/routes/example.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { test } from 'node:test' -import * as assert from 'node:assert' -import { build } from '../helper.js' - -test('example is loaded', async (t) => { - const app = await build(t) - - const res = await app.inject({ - url: '/example' - }) - - assert.equal(res.payload, 'this is an example') -}) diff --git a/src/icons/test/routes/root.test.ts b/src/icons/test/routes/root.test.ts deleted file mode 100644 index 743b610..0000000 --- a/src/icons/test/routes/root.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { test } from 'node:test' -import * as assert from 'node:assert' -import { build } from '../helper.js' - -test('default root route', async (t) => { - const app = await build(t) - - const res = await app.inject({ - url: '/' - }) - assert.deepStrictEqual(JSON.parse(res.payload), { root: true }) -}) diff --git a/src/icons/test/tsconfig.json b/src/icons/test/tsconfig.json deleted file mode 100644 index 3b443e0..0000000 --- a/src/icons/test/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": ".", - "noEmit": false - }, - "include": ["../src/**/*.ts", "**/*.ts"] -}