config: migrate from nestjs to tasks and websocket server in docker (#316)
* feat: make tasks script run in docker * feat: make websocket server work in docker * fix: format issue * fix: broken lockfile * fix: non matching typescript versions
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./client": "./src/client.ts",
|
||||
"./server": "./src/server.ts"
|
||||
"./server": "./src/server.ts",
|
||||
"./websocket": "./src/websocket.ts"
|
||||
},
|
||||
"private": true,
|
||||
"main": "./index.ts",
|
||||
@@ -12,12 +13,10 @@
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "pnpm with-env tsx ./src/wssDevServer.ts",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
@@ -30,14 +29,12 @@
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@trpc/client": "next",
|
||||
"@trpc/server": "next",
|
||||
"superjson": "2.2.1",
|
||||
"ws": "^8.16.0"
|
||||
"superjson": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/ws": "^8.5.10",
|
||||
"eslint": "^8.57.0",
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.4.4"
|
||||
|
||||
2
packages/api/src/websocket.ts
Normal file
2
packages/api/src/websocket.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { appRouter } from "./root";
|
||||
export { createTRPCContext } from "./trpc";
|
||||
@@ -1,64 +0,0 @@
|
||||
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
||||
import { WebSocketServer } from "ws";
|
||||
|
||||
import { getSessionFromToken, sessionTokenCookieName } from "@homarr/auth";
|
||||
import { parseCookies } from "@homarr/common";
|
||||
import { db } from "@homarr/db";
|
||||
import { logger } from "@homarr/log";
|
||||
|
||||
import { appRouter } from "./root";
|
||||
import { createTRPCContext } from "./trpc";
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
port: 3001,
|
||||
});
|
||||
const handler = applyWSSHandler({
|
||||
wss,
|
||||
router: appRouter,
|
||||
createContext: async ({ req }) => {
|
||||
try {
|
||||
const headers = Object.entries(req.headers).map(
|
||||
([key, value]) =>
|
||||
[key, typeof value === "string" ? value : value?.[0]] as [
|
||||
string,
|
||||
string,
|
||||
],
|
||||
);
|
||||
const nextHeaders = new Headers(headers);
|
||||
|
||||
const store = parseCookies(nextHeaders.get("cookie") ?? "");
|
||||
const sessionToken = store[sessionTokenCookieName];
|
||||
|
||||
const session = await getSessionFromToken(db, sessionToken);
|
||||
|
||||
return createTRPCContext({
|
||||
headers: nextHeaders,
|
||||
session,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return createTRPCContext({
|
||||
headers: new Headers(),
|
||||
session: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
wss.on("connection", (websocket, incomingMessage) => {
|
||||
logger.info(
|
||||
`➕ Connection (${wss.clients.size}) ${incomingMessage.method} ${incomingMessage.url}`,
|
||||
);
|
||||
websocket.once("close", (code, reason) => {
|
||||
logger.info(
|
||||
`➖ Connection (${wss.clients.size}) ${code} ${reason.toString()}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
logger.info("✅ WebSocket Server listening on ws://localhost:3001");
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
logger.info("SIGTERM");
|
||||
handler.broadcastReconnectNotification();
|
||||
wss.close();
|
||||
});
|
||||
@@ -36,6 +36,7 @@ export const createConfiguration = (isCredentialsRequest: boolean) =>
|
||||
session: sessionCallback,
|
||||
signIn: createSignInCallback(adapter, isCredentialsRequest),
|
||||
},
|
||||
secret: "secret-is-not-defined-yet", // TODO: This should be added later
|
||||
session: {
|
||||
strategy: "database",
|
||||
maxAge: sessionMaxAgeInSeconds,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
|
||||
const migrationsFolder = process.argv[2] ?? "./migrations";
|
||||
|
||||
const sqlite = new Database(process.env.DB_URL.replace("file:", ""));
|
||||
const sqlite = new Database(process.env.DB_URL?.replace("file:", ""));
|
||||
|
||||
const db = drizzle(sqlite);
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
"types": "./index.ts",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "esbuild migrate.ts --bundle --platform=node --outfile=migrate.cjs",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"migration:generate": "drizzle-kit generate:sqlite",
|
||||
"migration:run": "node ./migrate.mjs",
|
||||
"migration:run": "tsx ./migrate.ts",
|
||||
"push": "drizzle-kit push:sqlite",
|
||||
"studio": "drizzle-kit studio",
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -27,7 +27,7 @@ export class RedisTransport extends Transport {
|
||||
|
||||
this.redis
|
||||
.publish(
|
||||
"logging",
|
||||
"pubSub:logging",
|
||||
superjson.stringify({
|
||||
message: info.message,
|
||||
timestamp: info.timestamp,
|
||||
|
||||
@@ -37,7 +37,7 @@ export const createSubPubChannel = <TData>(name: string) => {
|
||||
);
|
||||
});
|
||||
subscriber.on("message", (channel, message) => {
|
||||
if (channel !== channelName) return;
|
||||
if (channel !== channelName) return; // TODO: check if this is necessary - it should be handled by the redis client
|
||||
|
||||
callback(superjson.parse(message));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user