feat(chat): added base for chat service
added front html nigel in the mud route function with openapi - gen clean up the code a little after pull request
This commit is contained in:
parent
73a4946d17
commit
9ce9fa44e4
122 changed files with 9354 additions and 2615 deletions
|
|
@ -11,20 +11,20 @@
|
|||
"dependencies": {
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@fastify/jwt": "^9.1.0",
|
||||
"@fastify/swagger": "^9.6.0",
|
||||
"@fastify/swagger": "^9.6.1",
|
||||
"@fastify/swagger-ui": "^5.2.3",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"better-sqlite3": "^11.10.0",
|
||||
"fastify": "^5.6.1",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"joi": "^18.0.1",
|
||||
"otp": "^1.1.2",
|
||||
"typebox": "^1.0.51",
|
||||
"typebox": "^1.0.53",
|
||||
"uuidv7": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/node": "^22.19.0"
|
||||
"@types/node": "^22.19.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export const authPlugin = fp<{ onlySchema?: boolean }>(async (fastify, { onlySch
|
|||
.clearCookie('token', { path: '/' })
|
||||
.makeResponse(401, 'notLoggedIn', 'auth.noUser');
|
||||
}
|
||||
req.authUser = { id: user.id, name: tok.who };
|
||||
req.authUser = { id: user.id, name: user.display_name };
|
||||
}
|
||||
catch {
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<!--
|
||||
<head>
|
||||
<title>Demo Page For Login :)</title>
|
||||
</head>
|
||||
|
|
@ -34,6 +34,6 @@
|
|||
</div>
|
||||
<pre id="d-response"></pre>
|
||||
<script src="./login_demo.js"> </script>
|
||||
</body>
|
||||
</body> -->
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,115 +1,4 @@
|
|||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const tUsername = document.querySelector('#t-username');
|
||||
|
||||
const iUsername = document.querySelector('#i-username');
|
||||
const iPassword = document.querySelector('#i-password');
|
||||
const iOtp = document.querySelector('#i-otp');
|
||||
|
||||
const bOtpSend = document.querySelector('#b-otpSend');
|
||||
const bLogin = document.querySelector('#b-login');
|
||||
const bLoginGuest = document.querySelector('#b-login-guest');
|
||||
const bLogout = document.querySelector('#b-logout');
|
||||
const bSignin = document.querySelector('#b-signin');
|
||||
const bWhoami = document.querySelector('#b-whoami');
|
||||
|
||||
const bOtpStatus = document.querySelector('#b-otpStatus');
|
||||
const bOtpEnable = document.querySelector('#b-otpEnable');
|
||||
const bOtpDisable = document.querySelector('#b-otpDisable');
|
||||
|
||||
const dResponse = document.querySelector('#d-response');
|
||||
|
||||
function setResponse(obj) {
|
||||
const obj_str = JSON.stringify(obj, null, 4);
|
||||
dResponse.innerText = obj_str;
|
||||
}
|
||||
let otpToken = null;
|
||||
|
||||
bLoginGuest.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/guest', { method: 'POST' });
|
||||
const json = await res.json();
|
||||
|
||||
setResponse(json);
|
||||
if (json.kind === 'success') {
|
||||
if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;}
|
||||
}
|
||||
});
|
||||
|
||||
bOtpSend.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/otp', { method: 'POST', body: JSON.stringify({ code: iOtp.value, token: otpToken }), headers });
|
||||
const json = await res.json();
|
||||
|
||||
setResponse(json);
|
||||
if (json.kind === 'success') {
|
||||
if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;}
|
||||
}
|
||||
});
|
||||
|
||||
bOtpStatus.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/statusOtp');
|
||||
const json = await res.json();
|
||||
|
||||
setResponse(json);
|
||||
});
|
||||
|
||||
bOtpEnable.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/enableOtp', { method: 'PUT' });
|
||||
const json = await res.json();
|
||||
|
||||
setResponse(json);
|
||||
});
|
||||
|
||||
bOtpDisable.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/disableOtp', { method: 'PUT' });
|
||||
const json = await res.json();
|
||||
|
||||
setResponse(json);
|
||||
});
|
||||
|
||||
bWhoami.addEventListener('click', async () => {
|
||||
let username = '';
|
||||
try {
|
||||
const res = await fetch('/api/user/info/me');
|
||||
const json = await res.json();
|
||||
setResponse(json);
|
||||
if (json?.kind === 'success') {username = json?.payload?.name;}
|
||||
else {username = `<not logged in:${json.msg}>`;}
|
||||
}
|
||||
catch {
|
||||
username = '<not logged in: threw>';
|
||||
}
|
||||
tUsername.innerText = username;
|
||||
});
|
||||
|
||||
bLogin.addEventListener('click', async () => {
|
||||
const name = iUsername.value;
|
||||
const password = iPassword.value;
|
||||
|
||||
const res = await fetch('/api/auth/login', { method: 'POST', body: JSON.stringify({ name, password }), headers });
|
||||
const json = await res.json();
|
||||
if (json?.kind === 'otpRequired') {
|
||||
otpToken = json?.payload?.token;
|
||||
}
|
||||
else if (json?.kind === 'success') {
|
||||
if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;}
|
||||
}
|
||||
setResponse(json);
|
||||
});
|
||||
|
||||
bLogout.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/auth/logout', { method: 'POST' });
|
||||
setResponse(await res.json());
|
||||
});
|
||||
|
||||
bSignin.addEventListener('click', async () => {
|
||||
const name = iUsername.value;
|
||||
const password = iPassword.value;
|
||||
|
||||
const res = await fetch('/api/auth/signin', { method: 'POST', body: JSON.stringify({ name, password }), headers });
|
||||
const json = await res.json();
|
||||
if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token};`;}
|
||||
setResponse(json);
|
||||
});
|
||||
// const headers = {
|
||||
// 'Accept': 'application/json',
|
||||
// 'Content-Type': 'application/json',
|
||||
// };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"version": "9.6.0",
|
||||
"version": "9.6.1",
|
||||
"title": "@fastify/swagger"
|
||||
},
|
||||
"components": {
|
||||
|
|
@ -144,26 +144,50 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failure"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"enableOtp.failure.noUser",
|
||||
"enableOtp.failure.noSecret"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -782,26 +806,52 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@
|
|||
"@fastify/multipart": "^9.3.0",
|
||||
"@fastify/sensible": "^6.0.3",
|
||||
"@fastify/static": "^8.3.0",
|
||||
"typebox": "^1.0.51",
|
||||
"confbox": "^0.2.2",
|
||||
"fastify": "^5.6.1",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-cli": "^7.4.1",
|
||||
"fastify-plugin": "^5.1.0"
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"typebox": "^1.0.53"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.0",
|
||||
"@types/node": "^22.19.1",
|
||||
"rollup-plugin-node-externals": "^8.1.2",
|
||||
"vite": "^7.2.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
|||
void req;
|
||||
void res;
|
||||
try {
|
||||
console.log('DEBUG ----- guest login backend');
|
||||
const adjective = getRandomFromList(fastify.words.adjectives);
|
||||
const noun = getRandomFromList(fastify.words.nouns);
|
||||
|
||||
|
|
|
|||
2
src/chat/.dockerignore
Normal file
2
src/chat/.dockerignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/dist
|
||||
/node_modules
|
||||
8
src/chat/README.md
Normal file
8
src/chat/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Nginx Configuration
|
||||
|
||||
You want to have a new microservice ?
|
||||
|
||||
Edit/add a file in `conf/locations/`
|
||||
take example on `conf/locations/icons.conf` on how to make a reverse proxy and on how to serve static files
|
||||
|
||||
# Good Luck Have Fun
|
||||
18
src/chat/entrypoint.sh
Normal file
18
src/chat/entrypoint.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
# do anything here
|
||||
|
||||
mkdir -p /volumes/static/chat/
|
||||
|
||||
cp -r /extra/chat.html /volumes/static/chat/chat.html
|
||||
|
||||
cp -r /extra/chat.js /volumes/static/chat/chat.js
|
||||
cp -r /extra/style.css /volumes/static/chat/style.css
|
||||
|
||||
|
||||
# chmod -R a+r /volumes/static/chat || true
|
||||
|
||||
# run the CMD [ ... ] from the dockerfile
|
||||
exec "$@"
|
||||
0
src/chat/extra/.gitkeep
Normal file
0
src/chat/extra/.gitkeep
Normal file
4
src/chat/extra/chat.html
Normal file
4
src/chat/extra/chat.html
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!-- OLD HTML to be deleted later -->
|
||||
</html>
|
||||
1
src/chat/extra/chat.js
Normal file
1
src/chat/extra/chat.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
// OLD JS file to be deleted later
|
||||
2
src/chat/extra/style.css
Normal file
2
src/chat/extra/style.css
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */
|
||||
/* Old css to be deleted later */
|
||||
130
src/chat/openapi.json
Normal file
130
src/chat/openapi.json
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"version": "9.6.1",
|
||||
"title": "@fastify/swagger"
|
||||
},
|
||||
"components": {
|
||||
"schemas": {}
|
||||
},
|
||||
"paths": {
|
||||
"/api/chat/test": {
|
||||
"get": {
|
||||
"operationId": "chatTest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg",
|
||||
"payload"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"success"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"chat.success"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"id",
|
||||
"guest"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"guest": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://local.maix.me:8888",
|
||||
"description": "direct from docker"
|
||||
},
|
||||
{
|
||||
"url": "https://local.maix.me:8000",
|
||||
"description": "using fnginx"
|
||||
}
|
||||
]
|
||||
}
|
||||
43
src/chat/package.json
Normal file
43
src/chat/package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"type": "module",
|
||||
"private": false,
|
||||
"name": "chat",
|
||||
"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",
|
||||
"build:openapi": "VITE_ENTRYPOINT=src/openapi.ts vite build && node dist/openapi.cjs >openapi.json"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fastify/autoload": "^6.3.1",
|
||||
"@fastify/formbody": "^8.0.2",
|
||||
"@fastify/multipart": "^9.3.0",
|
||||
"@fastify/sensible": "^6.0.3",
|
||||
"@fastify/static": "^8.3.0",
|
||||
"@fastify/websocket": "^11.2.0",
|
||||
"@sinclair/typebox": "^0.34.41",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"esbuild": "^0.24.2",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-cli": "^7.4.1",
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"jose": "^6.1.1",
|
||||
"sharp": "^0.34.5",
|
||||
"tailwindcss": "^4.1.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.1",
|
||||
"rollup-plugin-node-externals": "^8.1.2",
|
||||
"vite": "^7.2.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
45
src/chat/src/app.ts
Normal file
45
src/chat/src/app.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { FastifyPluginAsync } from 'fastify';
|
||||
import fastifyFormBody from '@fastify/formbody';
|
||||
import fastifyMultipart from '@fastify/multipart';
|
||||
import * as db from '@shared/database';
|
||||
import * as auth from '@shared/auth';
|
||||
import * as swagger from '@shared/swagger';
|
||||
import * as utils from '@shared/utils';
|
||||
|
||||
declare const __SERVICE_NAME: string;
|
||||
|
||||
// @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;
|
||||
await fastify.register(utils.useMakeResponse);
|
||||
await fastify.register(swagger.useSwagger, { service: __SERVICE_NAME });
|
||||
await fastify.register(db.useDatabase as FastifyPluginAsync, {});
|
||||
await fastify.register(auth.jwtPlugin as FastifyPluginAsync, {});
|
||||
await fastify.register(auth.authPlugin as FastifyPluginAsync, {});
|
||||
|
||||
// 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, {});
|
||||
}
|
||||
|
||||
void fastify.register(fastifyFormBody, {});
|
||||
void fastify.register(fastifyMultipart, {});
|
||||
fastify.get('/monitoring', () => 'Ok');
|
||||
};
|
||||
|
||||
export default app;
|
||||
export { app };
|
||||
21
src/chat/src/openapi.ts
Normal file
21
src/chat/src/openapi.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import f, { FastifyPluginAsync } from 'fastify';
|
||||
import * as swagger from '@shared/swagger';
|
||||
import * as auth from '@shared/auth';
|
||||
|
||||
declare const __SERVICE_NAME: string;
|
||||
|
||||
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||
const routes = import.meta.glob('./routes/**/*.ts', { eager: true });
|
||||
|
||||
async function start() {
|
||||
const fastify = f({ logger: false });
|
||||
await fastify.register(auth.authPlugin, { onlySchema: true });
|
||||
await fastify.register(swagger.useSwagger, { service: __SERVICE_NAME });
|
||||
|
||||
for (const route of Object.values(routes)) {
|
||||
await fastify.register(route as FastifyPluginAsync, {});
|
||||
}
|
||||
await fastify.ready();
|
||||
console.log(JSON.stringify(fastify.swagger(), undefined, 4));
|
||||
}
|
||||
start();
|
||||
16
src/chat/src/plugins/README.md
Normal file
16
src/chat/src/plugins/README.md
Normal file
|
|
@ -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/).
|
||||
10
src/chat/src/plugins/sensible.ts
Normal file
10
src/chat/src/plugins/sensible.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
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);
|
||||
});
|
||||
31
src/chat/src/routes/nginx-chat.ts
Normal file
31
src/chat/src/routes/nginx-chat.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { MakeStaticResponse, typeResponse } from '@shared/utils';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
|
||||
export const ChatRes = {
|
||||
200: typeResponse('success', 'chat.success', {
|
||||
name: Type.String(),
|
||||
id: Type.String(),
|
||||
guest: Type.Boolean(),
|
||||
}),
|
||||
};
|
||||
|
||||
export type ChatResType = MakeStaticResponse<typeof ChatRes>;
|
||||
|
||||
const route: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get(
|
||||
'/api/chat/test',
|
||||
{
|
||||
schema: {
|
||||
response: ChatRes,
|
||||
operationId: 'chatTest',
|
||||
},
|
||||
config: { requireAuth: true },
|
||||
},
|
||||
async (req, res) => {
|
||||
console.log('/api/chat called =================>');
|
||||
res.makeResponse(200, 'success', 'chat.success', { name: req.authUser!.name, 'id': req.authUser!.id, guest: false });
|
||||
},
|
||||
);
|
||||
};
|
||||
export default route;
|
||||
36
src/chat/src/run.ts
Normal file
36
src/chat/src/run.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// 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 });
|
||||
try {
|
||||
process.on('SIGTERM', () => {
|
||||
f.log.info('Requested to shutdown');
|
||||
process.exit(134);
|
||||
});
|
||||
console.log('-------->Serving static files from:');
|
||||
await f.register(app, {});
|
||||
await f.listen({ port: 80, host: '0.0.0.0' });
|
||||
}
|
||||
catch (err) {
|
||||
f.log.error(err);
|
||||
process.exit(1);
|
||||
};
|
||||
};
|
||||
start();
|
||||
15
src/chat/tsconfig.json
Normal file
15
src/chat/tsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// {
|
||||
// "extends": "../tsconfig.base.json",
|
||||
// "compilerOptions": {
|
||||
// "skipLibCheck": true, // skips type checking for all .d.ts files
|
||||
// "moduleResolution": "node",
|
||||
// "esModuleInterop": true,
|
||||
// "types": ["node"] },
|
||||
// "include": ["src/**/*.ts"]
|
||||
// }
|
||||
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
53
src/chat/vite.config.js
Normal file
53
src/chat/vite.config.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
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,
|
||||
define: {
|
||||
__SERVICE_NAME: '"chat"',
|
||||
},
|
||||
// service root
|
||||
plugins: [tsconfigPaths(), nodeExternals()],
|
||||
build: {
|
||||
ssr: true,
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, process.env.VITE_ENTRYPOINT ?? '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: true,
|
||||
minify: false,
|
||||
// for easier debugging
|
||||
},
|
||||
});
|
||||
|
|
@ -22,14 +22,14 @@
|
|||
"@fastify/multipart": "^9.3.0",
|
||||
"@fastify/sensible": "^6.0.3",
|
||||
"@fastify/static": "^8.3.0",
|
||||
"fastify": "^5.6.1",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-cli": "^7.4.1",
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"raw-body": "^3.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.0",
|
||||
"@types/node": "^22.19.1",
|
||||
"rollup-plugin-node-externals": "^8.1.2",
|
||||
"vite": "^7.2.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
|
|
|
|||
297
src/openapi.json
297
src/openapi.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"version": "9.6.0",
|
||||
"version": "9.6.1",
|
||||
"title": "@fastify/swagger"
|
||||
},
|
||||
"servers": [
|
||||
|
|
@ -160,26 +160,50 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"failure"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"enableOtp.failure.noUser",
|
||||
"enableOtp.failure.noSecret"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -816,26 +840,52 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -949,26 +999,52 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1030,6 +1106,117 @@
|
|||
"openapi_other"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/chat/test": {
|
||||
"get": {
|
||||
"operationId": "chatTest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg",
|
||||
"payload"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"success"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"chat.success"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"id",
|
||||
"guest"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"guest": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Default Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"openapi_other"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
"./@shared",
|
||||
"./icons",
|
||||
"./user",
|
||||
"./auth"
|
||||
"./auth",
|
||||
"./chat"
|
||||
],
|
||||
"lint-staged": {
|
||||
"*": [
|
||||
|
|
@ -26,18 +27,20 @@
|
|||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@openapitools/openapi-generator-cli": "^2.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.3",
|
||||
"@typescript-eslint/parser": "^8.46.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
||||
"@typescript-eslint/parser": "^8.46.4",
|
||||
"eslint": "^9.39.1",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.6",
|
||||
"openapi-generator-cli": "^1.0.0",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"rimraf": "^5.0.10",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.3"
|
||||
"typescript-eslint": "^8.46.4",
|
||||
"vite": "^7.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@redocly/cli": "^2.11.0",
|
||||
"@redocly/cli": "^2.11.1",
|
||||
"bindings": "^1.5.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1316
src/pnpm-lock.yaml
generated
1316
src/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,8 @@ apis:
|
|||
root: ./auth/openapi.json
|
||||
user:
|
||||
root: ./user/openapi.json
|
||||
chat:
|
||||
root: ./chat/openapi.json
|
||||
|
||||
rules:
|
||||
info-license: warn
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"version": "9.6.0",
|
||||
"version": "9.6.1",
|
||||
"title": "@fastify/swagger"
|
||||
},
|
||||
"components": {
|
||||
|
|
@ -85,26 +85,52 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"msg"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"enum": [
|
||||
"notLoggedIn"
|
||||
]
|
||||
},
|
||||
"msg": {
|
||||
"enum": [
|
||||
"auth.noCookie",
|
||||
"auth.invalidKind",
|
||||
"auth.noUser",
|
||||
"auth.invalid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@
|
|||
"@fastify/multipart": "^9.3.0",
|
||||
"@fastify/sensible": "^6.0.3",
|
||||
"@fastify/static": "^8.3.0",
|
||||
"typebox": "^1.0.51",
|
||||
"fastify": "^5.6.1",
|
||||
"fastify": "^5.6.2",
|
||||
"fastify-cli": "^7.4.1",
|
||||
"fastify-plugin": "^5.1.0"
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"typebox": "^1.0.53"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.0",
|
||||
"@types/node": "^22.19.1",
|
||||
"rollup-plugin-node-externals": "^8.1.2",
|
||||
"vite": "^7.2.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue