mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-13 15:35:00 +02:00
use config file instead of env
This commit is contained in:
parent
6fb569e2cd
commit
d9ae322e2a
19 changed files with 189 additions and 209 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -25,3 +25,4 @@ next-env.d.ts
|
||||||
migrations
|
migrations
|
||||||
package-lock.json
|
package-lock.json
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
config.yml
|
||||||
|
|
15
config/config.example.yml
Normal file
15
config/config.example.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
app:
|
||||||
|
name: Pangolin
|
||||||
|
environment: dev
|
||||||
|
base_url: http://localhost:3000
|
||||||
|
log_level: debug
|
||||||
|
save_logs: "false"
|
||||||
|
secure_cookies: "false"
|
||||||
|
|
||||||
|
server:
|
||||||
|
external_port: "3000"
|
||||||
|
internal_port: "3001"
|
||||||
|
|
||||||
|
rate_limit:
|
||||||
|
window_minutes: "1"
|
||||||
|
max_requests: "100"
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
import environment from "@server/environment";
|
import config, { APP_PATH } from "@server/config";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
@ -8,6 +8,6 @@ export default defineConfig({
|
||||||
out: path.join("server", "migrations"),
|
out: path.join("server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: path.join(environment.CONFIG_PATH, "db", "db.sqlite"),
|
url: path.join(APP_PATH, "db", "db.sqlite"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
"glob": "11.0.0",
|
"glob": "11.0.0",
|
||||||
"helmet": "7.1.0",
|
"helmet": "7.1.0",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
|
"js-yaml": "4.1.0",
|
||||||
"lucia": "3.2.0",
|
"lucia": "3.2.0",
|
||||||
"lucide-react": "0.447.0",
|
"lucide-react": "0.447.0",
|
||||||
"moment": "2.30.1",
|
"moment": "2.30.1",
|
||||||
|
@ -62,6 +63,7 @@
|
||||||
"@types/cookie-parser": "1.4.7",
|
"@types/cookie-parser": "1.4.7",
|
||||||
"@types/cors": "2.8.17",
|
"@types/cors": "2.8.17",
|
||||||
"@types/express": "5.0.0",
|
"@types/express": "5.0.0",
|
||||||
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/nodemailer": "6.4.16",
|
"@types/nodemailer": "6.4.16",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Lucia, TimeSpan } from "lucia";
|
||||||
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";
|
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { sessions, users } from "@server/db/schema";
|
import { sessions, users } from "@server/db/schema";
|
||||||
|
import config from "@server/config";
|
||||||
|
|
||||||
const adapter = new DrizzleSQLiteAdapter(db, sessions, users);
|
const adapter = new DrizzleSQLiteAdapter(db, sessions, users);
|
||||||
|
|
||||||
|
@ -18,19 +19,18 @@ export const lucia = new Lucia(adapter, {
|
||||||
dateCreated: attributes.dateCreated,
|
dateCreated: attributes.dateCreated,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// getSessionAttributes: (attributes) => {
|
|
||||||
// return {
|
|
||||||
// country: attributes.country,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
sessionCookie: {
|
sessionCookie: {
|
||||||
name: "session",
|
name: "session",
|
||||||
expires: false,
|
expires: false,
|
||||||
attributes: {
|
attributes: {
|
||||||
// secure: environment.ENVIRONMENT === "prod",
|
sameSite: "strict",
|
||||||
// sameSite: "strict",
|
secure: config.app.secure_cookies || false,
|
||||||
secure: false,
|
domain:
|
||||||
domain: ".testing123.io",
|
"." +
|
||||||
|
config.app.external_base_url
|
||||||
|
.split("://")[1]
|
||||||
|
.split(":")[0]
|
||||||
|
.split("/")[0],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sessionExpiresIn: new TimeSpan(2, "w"),
|
sessionExpiresIn: new TimeSpan(2, "w"),
|
||||||
|
@ -42,7 +42,6 @@ declare module "lucia" {
|
||||||
interface Register {
|
interface Register {
|
||||||
Lucia: typeof lucia;
|
Lucia: typeof lucia;
|
||||||
DatabaseUserAttributes: DatabaseUserAttributes;
|
DatabaseUserAttributes: DatabaseUserAttributes;
|
||||||
DatabaseSessionAttributes: DatabaseSessionAttributes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,3 @@ interface DatabaseUserAttributes {
|
||||||
emailVerified: boolean;
|
emailVerified: boolean;
|
||||||
dateCreated: string;
|
dateCreated: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DatabaseSessionAttributes {
|
|
||||||
// country: string;
|
|
||||||
}
|
|
||||||
|
|
85
server/config.ts
Normal file
85
server/config.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
|
export const APP_PATH = path.join("config");
|
||||||
|
|
||||||
|
const environmentSchema = z.object({
|
||||||
|
app: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
environment: z.enum(["dev", "prod"]),
|
||||||
|
external_base_url: z.string().url(),
|
||||||
|
internal_base_url: z.string().url(),
|
||||||
|
log_level: z.enum(["debug", "info", "warn", "error"]),
|
||||||
|
save_logs: z.string().transform((val) => val === "true"),
|
||||||
|
secure_cookies: z.string().transform((val) => val === "true"),
|
||||||
|
}),
|
||||||
|
server: z.object({
|
||||||
|
external_port: z
|
||||||
|
.string()
|
||||||
|
.transform((val) => parseInt(val, 10))
|
||||||
|
.pipe(z.number()),
|
||||||
|
internal_port: z
|
||||||
|
.string()
|
||||||
|
.transform((val) => parseInt(val, 10))
|
||||||
|
.pipe(z.number()),
|
||||||
|
}),
|
||||||
|
rate_limit: z.object({
|
||||||
|
window_minutes: z
|
||||||
|
.string()
|
||||||
|
.transform((val) => parseInt(val, 10))
|
||||||
|
.pipe(z.number()),
|
||||||
|
max_requests: z
|
||||||
|
.string()
|
||||||
|
.transform((val) => parseInt(val, 10))
|
||||||
|
.pipe(z.number()),
|
||||||
|
}),
|
||||||
|
email: z
|
||||||
|
.object({
|
||||||
|
smtp_host: z.string().optional(),
|
||||||
|
smtp_port: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
if (val) {
|
||||||
|
return parseInt(val, 10);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
})
|
||||||
|
.pipe(z.number().optional()),
|
||||||
|
smtp_user: z.string().optional(),
|
||||||
|
smtp_pass: z.string().optional(),
|
||||||
|
no_reply: z.string().email().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadConfig = (configPath: string) => {
|
||||||
|
try {
|
||||||
|
const yamlContent = fs.readFileSync(configPath, "utf8");
|
||||||
|
const config = yaml.load(yamlContent);
|
||||||
|
return config;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw new Error(
|
||||||
|
`Error loading configuration file: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const configFilePath = path.join(APP_PATH, "config.yml");
|
||||||
|
|
||||||
|
const environment = loadConfig(configFilePath);
|
||||||
|
|
||||||
|
const parsedConfig = environmentSchema.safeParse(environment);
|
||||||
|
|
||||||
|
if (!parsedConfig.success) {
|
||||||
|
const errors = fromError(parsedConfig.error);
|
||||||
|
throw new Error(`Invalid configuration file: ${errors}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default parsedConfig.data;
|
|
@ -1,10 +1,10 @@
|
||||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
import Database from "better-sqlite3";
|
import Database from "better-sqlite3";
|
||||||
import * as schema from "@server/db/schema";
|
import * as schema from "@server/db/schema";
|
||||||
import environment from "@server/environment";
|
import config, { APP_PATH } from "@server/config";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const location = path.join(environment.CONFIG_PATH, "db", "db.sqlite");
|
const location = path.join(APP_PATH, "db", "db.sqlite");
|
||||||
|
|
||||||
const sqlite = new Database(location);
|
const sqlite = new Database(location);
|
||||||
export const db = drizzle(sqlite, { schema });
|
export const db = drizzle(sqlite, { schema });
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
export * from "@server/emails/sendEmail";
|
export * from "@server/emails/sendEmail";
|
||||||
|
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import environment from "@server/environment";
|
import config from "@server/config";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
function createEmailClient() {
|
function createEmailClient() {
|
||||||
if (
|
if (
|
||||||
!environment.EMAIL_SMTP_HOST ||
|
!config.email?.smtp_host ||
|
||||||
!environment.EMAIL_SMTP_PORT ||
|
!config.email?.smtp_pass ||
|
||||||
!environment.EMAIL_SMTP_USER ||
|
!config.email?.smtp_port ||
|
||||||
!environment.EMAIL_SMTP_PASS
|
!config.email?.smtp_user
|
||||||
) {
|
) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Email SMTP configuration is missing. Emails will not be sent.",
|
"Email SMTP configuration is missing. Emails will not be sent.",
|
||||||
|
@ -18,12 +18,12 @@ function createEmailClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodemailer.createTransport({
|
return nodemailer.createTransport({
|
||||||
host: environment.EMAIL_SMTP_HOST,
|
host: config.email.smtp_host,
|
||||||
port: environment.EMAIL_SMTP_PORT,
|
port: config.email.smtp_port,
|
||||||
secure: false,
|
secure: false,
|
||||||
auth: {
|
auth: {
|
||||||
user: environment.EMAIL_SMTP_USER,
|
user: config.email.smtp_user,
|
||||||
pass: environment.EMAIL_SMTP_PASS,
|
pass: config.email.smtp_pass,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import logger from "@server/logger";
|
||||||
export async function sendEmail(
|
export async function sendEmail(
|
||||||
template: ReactElement,
|
template: ReactElement,
|
||||||
opts: {
|
opts: {
|
||||||
from: string;
|
from: string | undefined;
|
||||||
to: string;
|
to: string | undefined;
|
||||||
subject: string;
|
subject: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
import { z } from "zod";
|
|
||||||
import { fromError } from "zod-validation-error";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
const environmentSchema = z.object({
|
|
||||||
ENVIRONMENT: z.enum(["dev", "prod"]),
|
|
||||||
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]),
|
|
||||||
SAVE_LOGS: z.string().transform((val) => val === "true"),
|
|
||||||
CONFIG_PATH: z.string().transform((val) => {
|
|
||||||
// validate the path and remove any trailing slashes
|
|
||||||
const resolvedPath = path.resolve(val);
|
|
||||||
return resolvedPath.endsWith(path.sep)
|
|
||||||
? resolvedPath.slice(0, -1)
|
|
||||||
: resolvedPath;
|
|
||||||
}),
|
|
||||||
EXTERNAL_PORT: z
|
|
||||||
.string()
|
|
||||||
.transform((val) => parseInt(val, 10))
|
|
||||||
.pipe(z.number()),
|
|
||||||
INTERNAL_PORT: z
|
|
||||||
.string()
|
|
||||||
.transform((val) => parseInt(val, 10))
|
|
||||||
.pipe(z.number()),
|
|
||||||
RATE_LIMIT_WINDOW_MIN: z
|
|
||||||
.string()
|
|
||||||
.transform((val) => parseInt(val, 10))
|
|
||||||
.pipe(z.number()),
|
|
||||||
RATE_LIMIT_MAX: z
|
|
||||||
.string()
|
|
||||||
.transform((val) => parseInt(val, 10))
|
|
||||||
.pipe(z.number()),
|
|
||||||
APP_NAME: z.string(),
|
|
||||||
EMAIL_SMTP_HOST: z.string().optional(),
|
|
||||||
EMAIL_SMTP_PORT: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.transform((val) => {
|
|
||||||
if (val) {
|
|
||||||
return parseInt(val, 10);
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
})
|
|
||||||
.pipe(z.number().optional()),
|
|
||||||
EMAIL_SMTP_USER: z.string().optional(),
|
|
||||||
EMAIL_SMTP_PASS: z.string().optional(),
|
|
||||||
EMAIL_NOREPLY: z.string().email().optional(),
|
|
||||||
BASE_URL: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.transform((val) => {
|
|
||||||
if (!val) {
|
|
||||||
return `http://localhost:${environment.EXTERNAL_PORT}`;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
})
|
|
||||||
.pipe(z.string().url()),
|
|
||||||
});
|
|
||||||
|
|
||||||
const environment = {
|
|
||||||
ENVIRONMENT: (process.env.ENVIRONMENT as string) || "dev",
|
|
||||||
LOG_LEVEL: (process.env.LOG_LEVEL as string) || "debug",
|
|
||||||
SAVE_LOGS: (process.env.SAVE_LOGS as string) || "false",
|
|
||||||
CONFIG_PATH:
|
|
||||||
(process.env.CONFIG_PATH && path.join(process.env.CONFIG_PATH)) ||
|
|
||||||
path.join("config"),
|
|
||||||
EXTERNAL_PORT: (process.env.EXTERNAL_PORT as string) || "3000",
|
|
||||||
INTERNAL_PORT: (process.env.INTERNAL_PORT as string) || "3001",
|
|
||||||
RATE_LIMIT_WINDOW_MIN: (process.env.RATE_LIMIT_WINDOW_MIN as string) || "1",
|
|
||||||
RATE_LIMIT_MAX: (process.env.RATE_LIMIT_MAX as string) || "100",
|
|
||||||
APP_NAME: (process.env.APP_NAME as string) || "Pangolin",
|
|
||||||
EMAIL_SMTP_HOST: process.env.EMAIL_SMTP_HOST as string,
|
|
||||||
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT as string,
|
|
||||||
EMAIL_SMTP_USER: process.env.EMAIL_SMTP_USER as string,
|
|
||||||
EMAIL_SMTP_PASS: process.env.EMAIL_SMTP_PASS as string,
|
|
||||||
EMAIL_NOREPLY: process.env.EMAIL_NOREPLY as string,
|
|
||||||
BASE_URL: process.env.BASE_URL as string,
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsedConfig = environmentSchema.safeParse(environment);
|
|
||||||
|
|
||||||
if (!parsedConfig.success) {
|
|
||||||
const errors = fromError(parsedConfig.error);
|
|
||||||
throw new Error(`Invalid environment configuration: ${errors}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default parsedConfig.data;
|
|
|
@ -1,7 +1,7 @@
|
||||||
import express, { Request, Response } from "express";
|
import express, { Request, Response } from "express";
|
||||||
import next from "next";
|
import next from "next";
|
||||||
import { parse } from "url";
|
import { parse } from "url";
|
||||||
import environment from "@server/environment";
|
import config from "@server/config";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
|
@ -15,16 +15,16 @@ import { authenticated, unauthenticated } from "@server/routers/external";
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from "cookie-parser";
|
||||||
import { User } from "@server/db/schema";
|
import { User } from "@server/db/schema";
|
||||||
|
|
||||||
const dev = environment.ENVIRONMENT !== "prod";
|
const dev = config.app.environment !== "prod";
|
||||||
|
|
||||||
const app = next({ dev });
|
const app = next({ dev });
|
||||||
const handle = app.getRequestHandler();
|
const handle = app.getRequestHandler();
|
||||||
|
|
||||||
const externalPort = environment.EXTERNAL_PORT;
|
const externalPort = config.server.external_port;
|
||||||
const internalPort = environment.INTERNAL_PORT;
|
const internalPort = config.server.internal_port;
|
||||||
|
|
||||||
|
app.prepare().then(() => {
|
||||||
|
|
||||||
app.prepare().then(() => {
|
|
||||||
|
|
||||||
// External server
|
// External server
|
||||||
const externalServer = express();
|
const externalServer = express();
|
||||||
externalServer.set("trust proxy", 1);
|
externalServer.set("trust proxy", 1);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import "winston-daily-rotate-file";
|
import "winston-daily-rotate-file";
|
||||||
import environment from "@server/environment";
|
import config, { APP_PATH } from "@server/config";
|
||||||
import * as winston from "winston";
|
import * as winston from "winston";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
@ -24,11 +24,11 @@ const transports: any = [
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (environment.SAVE_LOGS) {
|
if (config.app.save_logs) {
|
||||||
transports.push(
|
transports.push(
|
||||||
new winston.transports.DailyRotateFile({
|
new winston.transports.DailyRotateFile({
|
||||||
filename: path.join(
|
filename: path.join(
|
||||||
environment.CONFIG_PATH,
|
APP_PATH,
|
||||||
"logs",
|
"logs",
|
||||||
"pangolin-%DATE%.log",
|
"pangolin-%DATE%.log",
|
||||||
),
|
),
|
||||||
|
@ -43,7 +43,7 @@ if (environment.SAVE_LOGS) {
|
||||||
transports.push(
|
transports.push(
|
||||||
new winston.transports.DailyRotateFile({
|
new winston.transports.DailyRotateFile({
|
||||||
filename: path.join(
|
filename: path.join(
|
||||||
environment.CONFIG_PATH,
|
APP_PATH,
|
||||||
"logs",
|
"logs",
|
||||||
".machinelogs-%DATE%.json",
|
".machinelogs-%DATE%.json",
|
||||||
),
|
),
|
||||||
|
@ -63,7 +63,7 @@ if (environment.SAVE_LOGS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: environment.LOG_LEVEL.toLowerCase(),
|
level: config.app.log_level.toLowerCase(),
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.splat(),
|
winston.format.splat(),
|
||||||
winston.format.timestamp(),
|
winston.format.timestamp(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ErrorRequestHandler, NextFunction, Response } from "express";
|
||||||
import ErrorResponse from "@server/types/ErrorResponse";
|
import ErrorResponse from "@server/types/ErrorResponse";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import environment from "@server/environment";
|
import config from "@server/config";
|
||||||
|
|
||||||
export const errorHandlerMiddleware: ErrorRequestHandler = (
|
export const errorHandlerMiddleware: ErrorRequestHandler = (
|
||||||
error,
|
error,
|
||||||
|
@ -11,7 +11,7 @@ export const errorHandlerMiddleware: ErrorRequestHandler = (
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
) => {
|
) => {
|
||||||
const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR;
|
const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR;
|
||||||
if (environment.ENVIRONMENT !== "prod") {
|
if (config.app.environment !== "prod") {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
res?.status(statusCode).send({
|
res?.status(statusCode).send({
|
||||||
|
@ -20,6 +20,6 @@ export const errorHandlerMiddleware: ErrorRequestHandler = (
|
||||||
error: true,
|
error: true,
|
||||||
message: error.message || "Internal Server Error",
|
message: error.message || "Internal Server Error",
|
||||||
status: statusCode,
|
status: statusCode,
|
||||||
stack: environment.ENVIRONMENT === "prod" ? null : error.stack,
|
stack: config.app.environment === "prod" ? null : error.stack,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { User, users } from "@server/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { verify } from "@node-rs/argon2";
|
import { verify } from "@node-rs/argon2";
|
||||||
import { createTOTPKeyURI } from "oslo/otp";
|
import { createTOTPKeyURI } from "oslo/otp";
|
||||||
import env from "@server/environment";
|
import config from "@server/config";
|
||||||
|
|
||||||
export const requestTotpSecretBody = z.object({
|
export const requestTotpSecretBody = z.object({
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
|
@ -65,7 +65,7 @@ export async function requestTotpSecret(
|
||||||
|
|
||||||
const hex = crypto.getRandomValues(new Uint8Array(20));
|
const hex = crypto.getRandomValues(new Uint8Array(20));
|
||||||
const secret = encodeHex(hex);
|
const secret = encodeHex(hex);
|
||||||
const uri = createTOTPKeyURI(env.APP_NAME, user.email, hex);
|
const uri = createTOTPKeyURI(config.app.name, user.email, hex);
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(users)
|
.update(users)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { users, emailVerificationCodes } from "@server/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { sendEmail } from "@server/emails";
|
import { sendEmail } from "@server/emails";
|
||||||
import VerifyEmail from "@server/emails/templates/verifyEmailCode";
|
import VerifyEmail from "@server/emails/templates/verifyEmailCode";
|
||||||
import env from "@server/environment";
|
import config from "@server/config";
|
||||||
|
|
||||||
export async function sendEmailVerificationCode(
|
export async function sendEmailVerificationCode(
|
||||||
email: string,
|
email: string,
|
||||||
|
@ -15,7 +15,7 @@ export async function sendEmailVerificationCode(
|
||||||
|
|
||||||
await sendEmail(VerifyEmail({ username: email, verificationCode: code }), {
|
await sendEmail(VerifyEmail({ username: email, verificationCode: code }), {
|
||||||
to: email,
|
to: email,
|
||||||
from: env.EMAIL_NOREPLY!,
|
from: config.email?.no_reply,
|
||||||
subject: "Verify your email address",
|
subject: "Verify your email address",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,6 @@ export async function verifyUser(
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const parsedBody = verifyUserBody.safeParse(req.query);
|
const parsedBody = verifyUserBody.safeParse(req.query);
|
||||||
|
|
||||||
logger.debug("Parsed body", parsedBody);
|
|
||||||
|
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
@ -40,9 +38,6 @@ export async function verifyUser(
|
||||||
try {
|
try {
|
||||||
const { session, user } = await lucia.validateSession(sessionId);
|
const { session, user } = await lucia.validateSession(sessionId);
|
||||||
|
|
||||||
logger.debug("Session", session);
|
|
||||||
logger.debug("User", user);
|
|
||||||
|
|
||||||
if (!session || !user) {
|
if (!session || !user) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(HttpCode.UNAUTHORIZED, "Invalid session"),
|
createHttpError(HttpCode.UNAUTHORIZED, "Invalid session"),
|
||||||
|
|
|
@ -5,14 +5,13 @@ import { DynamicTraefikConfig } from "./configSchema";
|
||||||
import { and, like, eq } from "drizzle-orm";
|
import { and, like, eq } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import env from "@server/environment";
|
import env from "@server/config";
|
||||||
import environment from "@server/environment";
|
import config from "@server/config";
|
||||||
|
|
||||||
export async function traefikConfigProvider(_: Request, res: Response) {
|
export async function traefikConfigProvider(_: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const targets = await getAllTargets();
|
const targets = await getAllTargets();
|
||||||
const traefikConfig = buildTraefikConfig(targets);
|
const traefikConfig = buildTraefikConfig(targets);
|
||||||
// logger.debug("Built traefik config");
|
|
||||||
res.status(HttpCode.OK).json(traefikConfig);
|
res.status(HttpCode.OK).json(traefikConfig);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`Failed to build traefik config: ${e}`);
|
logger.error(`Failed to build traefik config: ${e}`);
|
||||||
|
@ -32,35 +31,13 @@ export function buildTraefikConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
const http: DynamicTraefikConfig["http"] = {
|
const http: DynamicTraefikConfig["http"] = {
|
||||||
routers: {
|
routers: {},
|
||||||
"themainwebpage": {
|
services: {},
|
||||||
"entryPoints": [
|
|
||||||
"http"
|
|
||||||
],
|
|
||||||
"middlewares": [
|
|
||||||
],
|
|
||||||
"service": "service-themainwebpage",
|
|
||||||
"rule": "Host(`testing123.io`)"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
services: {
|
|
||||||
"service-themainwebpage": {
|
|
||||||
"loadBalancer": {
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"url": `http://${environment.APP_NAME.toLowerCase()}:3000`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
middlewares: {
|
middlewares: {
|
||||||
[middlewareName]: {
|
[middlewareName]: {
|
||||||
plugin: {
|
plugin: {
|
||||||
[middlewareName]: {
|
[middlewareName]: {
|
||||||
apiBaseUrl: `http://${environment.APP_NAME.toLowerCase()}:3001/api/v1`,
|
apiBaseUrl: config.app.internal_base_url,
|
||||||
// appBaseUrl: env.BASE_URL,
|
|
||||||
appBaseUrl: "http://testing123.io:8081",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
// const baseURL = `${window.location.protocol}//${window.location.host}/api/v1`;
|
|
||||||
|
|
||||||
|
|
||||||
export const api = axios.create({
|
export const api = axios.create({
|
||||||
baseURL: "http://testing123.io:8081/api/v1",
|
baseURL: process.env.NEXT_PUBLIC_EXTERNAL_API_BASE_URL,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -12,7 +9,7 @@ export const api = axios.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const internal = axios.create({
|
export const internal = axios.create({
|
||||||
baseURL: "http://pangolin:3000/api/v1",
|
baseURL: process.env.NEXT_PUBLIC_INTERNAL_API_BASE_URL,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
@ -4,49 +4,48 @@
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 37 100% 100%;
|
--background: 37 0% 95%;
|
||||||
--foreground: 37 5% 10%;
|
--foreground: 37 0% 10%;
|
||||||
--card: 37 50% 100%;
|
--card: 37 0% 90%;
|
||||||
--card-foreground: 37 5% 15%;
|
--card-foreground: 37 0% 15%;
|
||||||
--popover: 37 100% 100%;
|
--popover: 37 0% 95%;
|
||||||
--popover-foreground: 37 100% 10%;
|
--popover-foreground: 37 95% 10%;
|
||||||
--primary: 37 8% 51%;
|
--primary: 37 31% 25%;
|
||||||
--primary-foreground: 0 0% 100%;
|
--primary-foreground: 0 0% 100%;
|
||||||
--secondary: 37 30% 90%;
|
--secondary: 37 10% 74%;
|
||||||
--secondary-foreground: 0 0% 0%;
|
--secondary-foreground: 0 0% 0%;
|
||||||
--muted: -1 30% 95%;
|
--muted: -1 10% 85%;
|
||||||
--muted-foreground: 37 5% 40%;
|
--muted-foreground: 37 0% 40%;
|
||||||
--accent: -1 30% 90%;
|
--accent: -1 10% 80%;
|
||||||
--accent-foreground: 37 5% 15%;
|
--accent-foreground: 37 0% 15%;
|
||||||
--destructive: 0 100% 50%;
|
--destructive: 0 50% 50%;
|
||||||
--destructive-foreground: 37 5% 100%;
|
--destructive-foreground: 37 0% 90%;
|
||||||
--border: 37 30% 82%;
|
--border: 37 20% 74%;
|
||||||
--input: 37 30% 50%;
|
--input: 37 20% 50%;
|
||||||
--ring: 37 8% 51%;
|
--ring: 37 31% 25%;
|
||||||
--radius: 0rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 37 50% 10%;
|
--background: 37 10% 10%;
|
||||||
--foreground: 37 5% 100%;
|
--foreground: 37 0% 90%;
|
||||||
--card: 37 50% 10%;
|
--card: 37 0% 10%;
|
||||||
--card-foreground: 37 5% 100%;
|
--card-foreground: 37 0% 90%;
|
||||||
--popover: 37 50% 5%;
|
--popover: 37 10% 5%;
|
||||||
--popover-foreground: 37 5% 100%;
|
--popover-foreground: 37 0% 90%;
|
||||||
--primary: 37 8% 51%;
|
--primary: 37 31% 25%;
|
||||||
--primary-foreground: 0 0% 100%;
|
--primary-foreground: 0 0% 100%;
|
||||||
--secondary: 37 30% 20%;
|
--secondary: 37 10% 20%;
|
||||||
--secondary-foreground: 0 0% 100%;
|
--secondary-foreground: 0 0% 100%;
|
||||||
--muted: -1 30% 25%;
|
--muted: -1 10% 25%;
|
||||||
--muted-foreground: 37 5% 65%;
|
--muted-foreground: 37 0% 65%;
|
||||||
--accent: -1 30% 25%;
|
--accent: -1 10% 25%;
|
||||||
--accent-foreground: 37 5% 95%;
|
--accent-foreground: 37 0% 90%;
|
||||||
--destructive: 0 100% 50%;
|
--destructive: 0 50% 50%;
|
||||||
--destructive-foreground: 37 5% 100%;
|
--destructive-foreground: 37 0% 90%;
|
||||||
--border: 37 30% 50%;
|
--border: 37 20% 50%;
|
||||||
--input: 37 30% 50%;
|
--input: 37 20% 50%;
|
||||||
--ring: 37 8% 51%;
|
--ring: 37 31% 25%;
|
||||||
--radius: 0rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,4 +57,4 @@
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue