removed icon server and finished profile page
This commit is contained in:
parent
23baa4af56
commit
a8fa9f984d
26 changed files with 881 additions and 325 deletions
|
|
@ -8,6 +8,140 @@
|
|||
"schemas": {}
|
||||
},
|
||||
"paths": {
|
||||
"/api/auth/changePassword": {
|
||||
"post": {
|
||||
"operationId": "changePassword",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"new_password"
|
||||
],
|
||||
"properties": {
|
||||
"new_password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"success"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.success"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failed"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.failed.toolong",
|
||||
"changePassword.failed.tooshort",
|
||||
"changePassword.failed.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failed"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.failed.generic"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/auth/disableOtp": {
|
||||
"put": {
|
||||
"operationId": "disableOtp",
|
||||
|
|
|
|||
44
src/auth/src/routes/changePassword.ts
Normal file
44
src/auth/src/routes/changePassword.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { FastifyPluginAsync } from 'fastify';
|
||||
|
||||
import { Static, Type } from 'typebox';
|
||||
import { typeResponse, MakeStaticResponse } from '@shared/utils';
|
||||
|
||||
const ChangePasswordReq = Type.Object({
|
||||
new_password: Type.String(),
|
||||
});
|
||||
|
||||
type ChangePasswordReq = Static<typeof ChangePasswordReq>;
|
||||
|
||||
const ChangePasswordRes = {
|
||||
'500': typeResponse('failed',
|
||||
'changePassword.failed.generic'),
|
||||
'400': typeResponse('failed', [
|
||||
'changePassword.failed.toolong',
|
||||
'changePassword.failed.tooshort',
|
||||
'changePassword.failed.invalid',
|
||||
]),
|
||||
'200': typeResponse('success', 'changePassword.success'),
|
||||
};
|
||||
|
||||
type ChangePasswordRes = MakeStaticResponse<typeof ChangePasswordRes>;
|
||||
|
||||
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||
void _opts;
|
||||
fastify.post<{ Body: ChangePasswordReq }>(
|
||||
'/api/auth/changePassword',
|
||||
{ schema: { body: ChangePasswordReq, response: ChangePasswordRes, operationId: 'changePassword' }, config: { requireAuth: true } },
|
||||
async function(req, res) {
|
||||
const password = req.body.new_password;
|
||||
|
||||
if (password.length < 8) { return res.makeResponse(400, 'failed', 'changePassword.failed.tooshort'); }
|
||||
if (password.length > 64) { return res.makeResponse(400, 'failed', 'changePassword.failed.toolong'); }
|
||||
// password is good too !
|
||||
|
||||
await this.db.setUserPassword(req.authUser!.id, password);
|
||||
|
||||
return res.makeResponse(200, 'success', 'changePassword.success');
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export default route;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
/dist
|
||||
/node_modules
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
# do anything here
|
||||
|
||||
cp -r /extra /files
|
||||
|
||||
# run the CMD [ ... ] from the dockerfile
|
||||
exec "$@"
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"type": "module",
|
||||
"private": false,
|
||||
"name": "icons",
|
||||
"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.3.0",
|
||||
"@fastify/sensible": "^6.0.4",
|
||||
"@fastify/static": "^8.3.0",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-cli": "^7.4.1",
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"raw-body": "^3.0.2",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.2",
|
||||
"rollup-plugin-node-externals": "^8.1.2",
|
||||
"vite": "^7.2.7",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
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';
|
||||
import * as db from '@shared/database';
|
||||
import * as utils from '@shared/utils';
|
||||
import { authPlugin, jwtPlugin } from '@shared/auth';
|
||||
|
||||
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||
const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true });
|
||||
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||
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<void> => {
|
||||
void _opts;
|
||||
// Place here your custom code!
|
||||
for (const plugin of Object.values(plugins)) {
|
||||
void fastify.register(plugin as FastifyPluginAsync, {});
|
||||
}
|
||||
for (const route of Object.values(routes)) {
|
||||
void fastify.register(route as FastifyPluginAsync, {});
|
||||
}
|
||||
|
||||
await fastify.register(utils.useMonitoring);
|
||||
await fastify.register(db.useDatabase as FastifyPluginAsync, {});
|
||||
await fastify.register(authPlugin as FastifyPluginAsync, {});
|
||||
await fastify.register(jwtPlugin as FastifyPluginAsync, {});
|
||||
|
||||
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 (fastify2) => {
|
||||
const image_store = process.env.USER_ICONS_STORE ?? '/tmp/icons';
|
||||
fastify2.decorate('image_store', image_store);
|
||||
await mkdir(fastify2.image_store, { recursive: true });
|
||||
}));
|
||||
};
|
||||
|
||||
export default app;
|
||||
export { app };
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# 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/).
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
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<FastifySensibleOptions>(async (fastify) => {
|
||||
fastify.register(sensible);
|
||||
});
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { join } from 'node:path';
|
||||
import { open } from 'node:fs/promises';
|
||||
import sharp from 'sharp';
|
||||
import rawBody from 'raw-body';
|
||||
import { isNullish } from '@shared/utils';
|
||||
|
||||
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||
void _opts;
|
||||
// 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: `/<USERID_HERE>`
|
||||
// it sets some configuration options, and set the actual function that will handle the request
|
||||
|
||||
fastify.addContentTypeParser('*', function(request, payload, done) {
|
||||
done(null);
|
||||
});
|
||||
|
||||
fastify.post<{ Params: { userid: string } }>('/:userid', async function(request, reply) {
|
||||
const buffer = await rawBody(request.raw);
|
||||
// this is how we get the `:userid` part of things
|
||||
const userid: string | undefined = (request.params)['userid'];
|
||||
if (isNullish(userid)) {
|
||||
return await reply.code(403);
|
||||
}
|
||||
const image_store: string = fastify.getDecorator('image_store');
|
||||
const image_path = join(image_store, userid);
|
||||
|
||||
try {
|
||||
const img = sharp(buffer);
|
||||
img.resize({
|
||||
height: 128,
|
||||
width: 128,
|
||||
fit: 'fill',
|
||||
});
|
||||
const data = await img.png({ compressionLevel: 6 }).toBuffer();
|
||||
const image_file = await open(image_path, 'w', 0o666);
|
||||
await image_file.write(data);
|
||||
await image_file.close();
|
||||
}
|
||||
catch (e) {
|
||||
fastify.log.error(`Error: ${e}`);
|
||||
reply.code(400);
|
||||
return { status: 'error' };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default route;
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// this sould only be used by the docker file !
|
||||
|
||||
import fastify, { FastifyInstance } from 'fastify';
|
||||
import app from './app';
|
||||
|
||||
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 });
|
||||
process.on('SIGTERM', () => {
|
||||
f.log.info('Requested to shutdown');
|
||||
process.exit(134);
|
||||
});
|
||||
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();
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import nodeExternals from 'rollup-plugin-node-externals';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
|
||||
function collectDeps(...pkgJsonPaths) {
|
||||
const allDeps = new Set();
|
||||
for (const pkgPath of pkgJsonPaths) {
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
||||
for (const dep of Object.keys(pkg.dependencies || {})) {
|
||||
allDeps.add(dep);
|
||||
}
|
||||
for (const peer of Object.keys(pkg.peerDependencies || {})) {
|
||||
allDeps.add(peer);
|
||||
}
|
||||
}
|
||||
return Array.from(allDeps);
|
||||
}
|
||||
|
||||
const externals = collectDeps(
|
||||
'./package.json',
|
||||
'../@shared/package.json',
|
||||
);
|
||||
|
||||
|
||||
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: externals,
|
||||
},
|
||||
target: 'node22',
|
||||
// or whatever Node version you use
|
||||
sourcemap: false,
|
||||
minify: true,
|
||||
// for easier debugging
|
||||
},
|
||||
});
|
||||
137
src/openapi.json
137
src/openapi.json
|
|
@ -21,6 +21,143 @@
|
|||
}
|
||||
],
|
||||
"paths": {
|
||||
"/api/auth/changePassword": {
|
||||
"post": {
|
||||
"operationId": "changePassword",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"new_password"
|
||||
],
|
||||
"properties": {
|
||||
"new_password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"success"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.success"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failed"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.failed.toolong",
|
||||
"changePassword.failed.tooshort",
|
||||
"changePassword.failed.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failed"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"changePassword.failed.generic"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"openapi_other"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/auth/disableOtp": {
|
||||
"put": {
|
||||
"operationId": "disableOtp",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue