feat: custom server that works

This commit is contained in:
Derock 2023-11-10 20:35:55 -05:00
parent 2e86e827ca
commit e151a91290
No known key found for this signature in database
17 changed files with 1372 additions and 284 deletions

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ next-env.d.ts
# production
/build
/dist
# misc
.DS_Store

View file

@ -1,10 +1,9 @@
import type { Config } from "drizzle-kit";
import { env } from "~/env.mjs";
export default {
schema: "./src/server/db/schema.ts",
driver: "better-sqlite",
dbCredentials: {
url: env.DATABASE_PATH,
url: process.env.DATABASE_PATH ?? "./data/db.sqlite",
},
} satisfies Config;

View file

@ -1,9 +1,3 @@
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
* for Docker builds.
*/
await import("./src/env.mjs");
/** @type {import("next").NextConfig} */
const config = {
experimental: {

View file

@ -2,15 +2,20 @@
"name": "hostforge",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "next build",
"build:next": "next build",
"build:server-tsc": "tsc -p tsconfig.server.json && tscpaths -p tsconfig.server.json -s ./src -o ./dist/",
"build:server": "tsup",
"clean": "rm -rf .next dist",
"db:push": "drizzle-kit push:sqlite",
"dev": "tsx watch src/server/server.ts",
"dev": "npm-run-all build:server dev:run",
"dev:run": "node --enable-source-maps dist/server.js",
"lint": "next lint",
"start": "next start"
"start": "node -r tsconfig"
},
"dependencies": {
"@mantine/form": "^7.1.7",
"@mantine/form": "^7.2.1",
"@prisma/migrate": "^5.5.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
@ -32,14 +37,15 @@
"date-fns": "^2.30.0",
"dotenv": "^16.3.1",
"drizzle-orm": "^0.28.6",
"extensionless": "^1.7.3",
"ipaddr.js": "^2.1.0",
"next": "^14.0.1",
"next": "14.0.1",
"next-themes": "^0.2.1",
"node-os-utils": "^1.3.7",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.11.0",
"recharts": "^2.9.2",
"recharts": "^2.9.3",
"sonner": "^1.2.0",
"superjson": "^2.2.0",
"tailwind-merge": "^2.0.0",
@ -51,26 +57,31 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.6",
"@types/bunyan": "^1.8.10",
"@types/bunyan-format": "^0.2.7",
"@types/eslint": "^8.44.6",
"@types/node": "^20.8.10",
"@types/better-sqlite3": "^7.6.7",
"@types/bunyan": "^1.8.11",
"@types/bunyan-format": "^0.2.8",
"@types/eslint": "^8.44.7",
"@types/node": "^20.9.0",
"@types/node-os-utils": "^1.3.4",
"@types/react": "^18.2.35",
"@types/react-dom": "^18.2.14",
"@types/ua-parser-js": "^0.7.38",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/ua-parser-js": "^0.7.39",
"@types/ws": "^8.5.9",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"autoprefixer": "^10.4.16",
"drizzle-kit": "^0.19.13",
"eslint": "^8.53.0",
"eslint-config-next": "^14.0.1",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.31",
"prettier": "^3.0.3",
"prettier-plugin-tailwindcss": "^0.5.6",
"tailwindcss": "^3.3.5",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"tscpaths": "^0.0.9",
"tsup": "^7.2.0",
"tsx": "^3.14.0",
"typescript": "^5.2.2"
},

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextRequest } from "next/server";
import { type NextRequest } from "next/server.js";
import { env } from "~/env.mjs";
import { env } from "~/env";
import { appRouter } from "~/server/api/root";
import { createTRPCContext } from "~/server/api/trpc";

View file

@ -1,11 +1,10 @@
"use client";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { RiPulseFill } from "react-icons/ri";
import { ResponsiveContainer, AreaChart, Area } from "recharts";
import styles from "./StatCard.module.css";
// import styles from "./StatCard.module.css";
export function StatCard<T extends { [key: string]: number }>(props: {
export function StatCard<T extends Record<string, number>>(props: {
title: string;
value: string;
subvalue: string;
@ -73,7 +72,7 @@ export function StatCard<T extends { [key: string]: number }>(props: {
</ResponsiveContainer>
</div>
<div className={`relative z-10 ${styles["stat-card"]}`}>
<div className={`relative z-10 ${/*styles["stat-card"]*/ "asdf"}`}>
<p className="stroke stroke-card text-2xl font-bold">{props.value}</p>
<p className="stroke stroke-card text-sm text-muted-foreground">
{props.subvalue}

View file

@ -7,6 +7,7 @@ export const env = createEnv({
* isn't built with invalid env vars.
*/
server: {
// make sure to update drizzle.config.js if you change this
DATABASE_PATH: z.string().default("./data/db.sqlite"),
NODE_ENV: z
@ -16,7 +17,10 @@ export const env = createEnv({
SQLITE_UUIDV7_EXT_PATH: z.string().optional(),
SESSION_SECRET: z.string().min(8),
HOSTNAME: z.string().default("localhost"),
PORT: z.number().default(3000),
PORT: z
.string()
.default("3000")
.transform((str) => parseInt(str)),
},
/**

View file

@ -1,6 +1,7 @@
import pacakge from "../package.json";
import { env } from "./env.mjs";
const { version } = pacakge;
// import pacakge from "../package.json";
import { env } from "~/env";
// const { version } = pacakge;
const version = "1.0.0";
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
@ -8,15 +9,17 @@ export async function register() {
const { migrate } = await import("drizzle-orm/better-sqlite3/migrator");
const { db } = await import("./server/db");
const { mkdir, stat } = await import("fs/promises");
const { dirname } = await import("path");
const path = await import("path");
// check if database folder exists
try {
const dir = dirname(env.DATABASE_PATH);
const dir = path.dirname(env.DATABASE_PATH);
await stat(dir);
} catch (e) {
await mkdir(dirname(env.DATABASE_PATH), { recursive: true });
logger.debug(`Created database folder ${dirname(env.DATABASE_PATH)}`);
await mkdir(path.dirname(env.DATABASE_PATH), { recursive: true });
logger.debug(
`Created database folder ${path.dirname(env.DATABASE_PATH)}`,
);
}
if (env.NODE_ENV === "production") {

View file

@ -7,7 +7,7 @@
* need to use are documented accordingly near the end.
*/
import { TRPCError, initTRPC } from "@trpc/server";
import { type NextRequest } from "next/server";
import { type NextRequest } from "next/server.js";
import superjson from "superjson";
import { ZodError } from "zod";
import { db } from "~/server/db";

View file

@ -3,9 +3,9 @@ import { db } from "../db";
import { users, sessions } from "../db/schema";
import { randomBytes } from "crypto";
import assert from "assert";
import { NextRequest, userAgent } from "next/server";
import { NextRequest, userAgent } from "next/server.js";
import { hash as argon2Hash } from "argon2";
import { env } from "~/env.mjs";
import { env } from "~/env";
export type SessionUpdateData = Partial<{
ua: string;

View file

@ -1,7 +1,7 @@
import { drizzle } from "drizzle-orm/better-sqlite3";
import SQLite3 from "better-sqlite3";
import { join } from "path";
import { env } from "~/env.mjs";
import { env } from "~/env";
const sqlite = new SQLite3(env.DATABASE_PATH);

View file

@ -1,10 +1,12 @@
// import { config } from "dotenv";
// config();
import "dotenv/config";
import next from "next";
import { env } from "~/env.mjs";
import { env } from "~/env";
import { createServer } from "http";
import logger from "./utils/logger";
import ws from "ws";
// import ws from "ws";
import { WebSocketServer } from "ws";
import { applyWSSHandler } from "@trpc/server/adapters/ws";
import { appRouter } from "./api/root";
import { createTRPCContext } from "./api/trpc";
@ -16,6 +18,9 @@ async function startApp() {
dev: env.NODE_ENV !== "production",
hostname: env.HOSTNAME,
port: env.PORT,
// dir: path.join(__dirname, "../.."),
customServer: true,
isNodeDebugging: true,
});
await app.prepare();
@ -26,18 +31,15 @@ async function startApp() {
// create the http server
const server = createServer((req, res) => {
try {
// handle the request
getHandler(req, res);
} catch (error) {
getHandler(req, res).catch((error) => {
logger.error(error);
res.statusCode = 500;
res.end("Internal Server Error");
}
});
});
// create the websocket server
const wss = new ws.Server({ noServer: true });
const wss = new WebSocketServer({ noServer: true });
const trpcHandler = applyWSSHandler({
wss,
router: appRouter,
@ -49,6 +51,7 @@ async function startApp() {
});
process.on("SIGTERM", () => {
logger.warn("SIGTERM received, shutting down...");
trpcHandler.broadcastReconnectNotification();
server.close(() => {
process.exit(0);
@ -59,9 +62,9 @@ async function startApp() {
server.on("upgrade", (req, socket, head) => {
// send trpc requests to the trpc server
if (req.url?.startsWith("/api/trpc")) {
wss.handleUpgrade(req, socket, head, () => {});
wss.handleUpgrade(req, socket, head, () => undefined);
} else {
upgradeHandler(req, socket, head);
void upgradeHandler(req, socket, head);
}
});
@ -71,4 +74,4 @@ async function startApp() {
});
}
startApp();
void startApp();

View file

@ -1,6 +1,6 @@
import assert from "assert";
import { IncomingMessage } from "http";
import { NextRequest } from "next/server";
import { type IncomingMessage } from "http";
import { NextRequest } from "next/server.js";
/**
* Turns an node:http IncomingMessage into a next.js request

View file

@ -1,9 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"module": "ES2020",
"outDir": "dist",
"noEmit": false
"moduleResolution": "node",
"target": "ESNext",
"lib": ["ESNext", "DOM"],
"isolatedModules": false,
"noEmit": false,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"]
},
"allowSyntheticDefaultImports": true,
"sourceMap": true
},
"include": ["./src/**/*"]
"include": ["next-env.d.ts", "src/server/**/*.ts", "src/server/**/*.tsx"]
}

17
tsup.config.ts Normal file
View file

@ -0,0 +1,17 @@
import { defineConfig, type Options } from "tsup";
const opts: Options = {
platform: "node",
format: ["esm"],
treeshake: true,
clean: true,
sourcemap: true,
tsconfig: "tsconfig.server.json",
};
export default defineConfig([
{
entryPoints: ["src/server/server.ts"],
...opts,
},
]);