feat: custom server that works
This commit is contained in:
parent
2e86e827ca
commit
e151a91290
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,6 +18,7 @@ next-env.d.ts
|
|||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
43
package.json
43
package.json
|
@ -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"
|
||||
},
|
||||
|
|
1496
pnpm-lock.yaml
1496
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -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";
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)),
|
||||
},
|
||||
|
||||
/**
|
|
@ -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") {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
17
tsup.config.ts
Normal 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,
|
||||
},
|
||||
]);
|
Loading…
Reference in a new issue