diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index 2b10d2af..8fc99161 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -2,15 +2,10 @@ import { defineConfig } from "drizzle-kit"; import path from "path"; import { build } from "@server/build"; -let schema; -if (build === "oss") { - schema = [path.join("server", "db", "pg", "schema.ts")]; -} else { - schema = [ - path.join("server", "db", "pg", "schema.ts"), - path.join("server", "db", "pg", "privateSchema.ts") - ]; -} +const schema = [ + path.join("server", "db", "pg", "schema.ts"), + path.join("server", "db", "pg", "pSchema.ts") +]; export default defineConfig({ dialect: "postgresql", diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index 25bbe7f3..b8679aa9 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -3,15 +3,10 @@ import { APP_PATH } from "@server/lib/consts"; import { defineConfig } from "drizzle-kit"; import path from "path"; -let schema; -if (build === "oss") { - schema = [path.join("server", "db", "sqlite", "schema.ts")]; -} else { - schema = [ - path.join("server", "db", "sqlite", "schema.ts"), - path.join("server", "db", "sqlite", "privateSchema.ts") - ]; -} +const schema = [ + path.join("server", "db", "sqlite", "schema.ts"), + path.join("server", "db", "sqlite", "pSchema.ts") +]; export default defineConfig({ dialect: "sqlite", diff --git a/esbuild.mjs b/esbuild.mjs index 8086a77e..7f67fe81 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -2,8 +2,9 @@ import esbuild from "esbuild"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { nodeExternalsPlugin } from "esbuild-node-externals"; +import path from "path"; +import fs from "fs"; // import { glob } from "glob"; -// import path from "path"; const banner = ` // patch __dirname @@ -18,7 +19,7 @@ const require = topLevelCreateRequire(import.meta.url); `; const argv = yargs(hideBin(process.argv)) - .usage("Usage: $0 -entry [string] -out [string]") + .usage("Usage: $0 -entry [string] -out [string] -build [string]") .option("entry", { alias: "e", describe: "Entry point file", @@ -31,6 +32,13 @@ const argv = yargs(hideBin(process.argv)) type: "string", demandOption: true, }) + .option("build", { + alias: "b", + describe: "Build type (oss, saas, enterprise)", + type: "string", + choices: ["oss", "saas", "enterprise"], + default: "oss", + }) .help() .alias("help", "h").argv; @@ -46,6 +54,179 @@ function getPackagePaths() { return ["package.json"]; } +// Plugin to guard against bad imports from #private +function privateImportGuardPlugin() { + return { + name: "private-import-guard", + setup(build) { + const violations = []; + + build.onResolve({ filter: /^#private\// }, (args) => { + const importingFile = args.importer; + + // Check if the importing file is NOT in server/private + const normalizedImporter = path.normalize(importingFile); + const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + + if (!isInServerPrivate) { + const violation = { + file: importingFile, + importPath: args.path, + resolveDir: args.resolveDir + }; + violations.push(violation); + + console.log(`PRIVATE IMPORT VIOLATION:`); + console.log(` File: ${importingFile}`); + console.log(` Import: ${args.path}`); + console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); + console.log(''); + } + + // Return null to let the default resolver handle it + return null; + }); + + build.onEnd((result) => { + if (violations.length > 0) { + console.log(`\nSUMMARY: Found ${violations.length} private import violation(s):`); + violations.forEach((v, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + }); + console.log(''); + + result.errors.push({ + text: `Private import violations detected: ${violations.length} violation(s) found`, + location: null, + notes: violations.map(v => ({ + text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, + location: null + })) + }); + } + }); + } + }; +} + +// Plugin to guard against bad imports from #private +function dynamicImportGuardPlugin() { + return { + name: "dynamic-import-guard", + setup(build) { + const violations = []; + + build.onResolve({ filter: /^#dynamic\// }, (args) => { + const importingFile = args.importer; + + // Check if the importing file is NOT in server/private + const normalizedImporter = path.normalize(importingFile); + const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + + if (isInServerPrivate) { + const violation = { + file: importingFile, + importPath: args.path, + resolveDir: args.resolveDir + }; + violations.push(violation); + + console.log(`DYNAMIC IMPORT VIOLATION:`); + console.log(` File: ${importingFile}`); + console.log(` Import: ${args.path}`); + console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); + console.log(''); + } + + // Return null to let the default resolver handle it + return null; + }); + + build.onEnd((result) => { + if (violations.length > 0) { + console.log(`\nSUMMARY: Found ${violations.length} dynamic import violation(s):`); + violations.forEach((v, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + }); + console.log(''); + + result.errors.push({ + text: `Dynamic import violations detected: ${violations.length} violation(s) found`, + location: null, + notes: violations.map(v => ({ + text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, + location: null + })) + }); + } + }); + } + }; +} + +// Plugin to dynamically switch imports based on build type +function dynamicImportSwitcherPlugin(buildValue) { + return { + name: "dynamic-import-switcher", + setup(build) { + const switches = []; + + build.onStart(() => { + console.log(`Dynamic import switcher using build type: ${buildValue}`); + }); + + build.onResolve({ filter: /^#dynamic\// }, (args) => { + // Extract the path after #dynamic/ + const dynamicPath = args.path.replace(/^#dynamic\//, ''); + + // Determine the replacement based on build type + let replacement; + if (buildValue === "oss") { + replacement = `#open/${dynamicPath}`; + } else if (buildValue === "saas" || buildValue === "enterprise") { + replacement = `#closed/${dynamicPath}`; // We use #closed here so that the route guards dont complain after its been changed but this is the same as #private + } else { + console.warn(`Unknown build type '${buildValue}', defaulting to #open/`); + replacement = `#open/${dynamicPath}`; + } + + const switchInfo = { + file: args.importer, + originalPath: args.path, + replacementPath: replacement, + buildType: buildValue + }; + switches.push(switchInfo); + + console.log(`DYNAMIC IMPORT SWITCH:`); + console.log(` File: ${args.importer}`); + console.log(` Original: ${args.path}`); + console.log(` Switched to: ${replacement} (build: ${buildValue})`); + console.log(''); + + // Rewrite the import path and let the normal resolution continue + return build.resolve(replacement, { + importer: args.importer, + namespace: args.namespace, + resolveDir: args.resolveDir, + kind: args.kind + }); + }); + + build.onEnd((result) => { + if (switches.length > 0) { + console.log(`\nDYNAMIC IMPORT SUMMARY: Switched ${switches.length} import(s) for build type '${buildValue}':`); + switches.forEach((s, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), s.file)}`); + console.log(` ${s.originalPath} → ${s.replacementPath}`); + }); + console.log(''); + } + }); + } + }; +} + esbuild .build({ entryPoints: [argv.entry], @@ -59,6 +240,9 @@ esbuild platform: "node", external: ["body-parser"], plugins: [ + privateImportGuardPlugin(), + dynamicImportGuardPlugin(), + dynamicImportSwitcherPlugin(argv.build), nodeExternalsPlugin({ packagePath: getPackagePaths(), }), @@ -66,7 +250,27 @@ esbuild sourcemap: "inline", target: "node22", }) - .then(() => { + .then((result) => { + // Check if there were any errors in the build result + if (result.errors && result.errors.length > 0) { + console.error(`Build failed with ${result.errors.length} error(s):`); + result.errors.forEach((error, i) => { + console.error(`${i + 1}. ${error.text}`); + if (error.notes) { + error.notes.forEach(note => { + console.error(` - ${note.text}`); + }); + } + }); + + // remove the output file if it was created + if (fs.existsSync(argv.out)) { + fs.unlinkSync(argv.out); + } + + process.exit(1); + } + console.log("Build completed successfully"); }) .catch((error) => { diff --git a/server/apiServer.ts b/server/apiServer.ts index 0b5a6305..9a626769 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -7,21 +7,21 @@ import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares"; -import { corsWithLoginPageSupport } from "@server/middlewares/private/corsWithLoginPage"; -import { authenticated, unauthenticated } from "@server/routers/external"; -import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws"; +import { authenticated, unauthenticated } from "#dynamic/routers/external"; +import { router as wsRouter, handleWSUpgrade } from "#dynamic/routers/ws"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; import { csrfProtectionMiddleware } from "./middlewares/csrfProtection"; import helmet from "helmet"; -import { stripeWebhookHandler } from "@server/routers/private/billing/webhooks"; import { build } from "./build"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import HttpCode from "./types/HttpCode"; import requestTimeoutMiddleware from "./middlewares/requestTimeout"; -import { createStore } from "@server/lib/private/rateLimitStore"; -import hybridRouter from "@server/routers/private/hybrid"; +import { createStore } from "#dynamic/lib/rateLimitStore"; import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; +import { corsWithLoginPageSupport } from "@server/lib/corsWithLoginPage"; +import { hybridRouter } from "#dynamic/routers/hybrid"; +import { billingWebhookHandler } from "#dynamic/routers/billing/webhooks"; const dev = config.isDev; const externalPort = config.getRawConfig().server.external_port; @@ -39,7 +39,7 @@ export function createApiServer() { apiServer.post( `${prefix}/billing/webhooks`, express.raw({ type: "application/json" }), - stripeWebhookHandler + billingWebhookHandler ); } diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 668be0db..e48bc502 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -4,7 +4,6 @@ import { userActions, roleActions, userOrgs } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { sendUsageNotification } from "@server/routers/org"; export enum ActionsEnum { createOrgUser = "createOrgUser", diff --git a/server/cleanup.ts b/server/cleanup.ts new file mode 100644 index 00000000..de54ed77 --- /dev/null +++ b/server/cleanup.ts @@ -0,0 +1,13 @@ +import { cleanup as wsCleanup } from "@server/routers/ws"; + +async function cleanup() { + await wsCleanup(); + + process.exit(0); +} + +export async function initCleanup() { + // Handle process termination + process.on("SIGTERM", () => cleanup()); + process.on("SIGINT", () => cleanup()); +} \ No newline at end of file diff --git a/server/db/pg/privateSchema.ts b/server/db/pg/privateSchema.ts index 8ea8f9de..67fb28ec 100644 --- a/server/db/pg/privateSchema.ts +++ b/server/db/pg/privateSchema.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { pgTable, serial, diff --git a/server/db/sqlite/privateSchema.ts b/server/db/sqlite/privateSchema.ts index fbe86e25..557ebfd6 100644 --- a/server/db/sqlite/privateSchema.ts +++ b/server/db/sqlite/privateSchema.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { sqliteTable, integer, diff --git a/server/emails/sendEmail.ts b/server/emails/sendEmail.ts index 24fb4fbd..c8a0b077 100644 --- a/server/emails/sendEmail.ts +++ b/server/emails/sendEmail.ts @@ -2,7 +2,6 @@ import { render } from "@react-email/render"; import { ReactElement } from "react"; import emailClient from "@server/emails"; import logger from "@server/logger"; -import config from "@server/lib/config"; export async function sendEmail( template: ReactElement, @@ -25,7 +24,7 @@ export async function sendEmail( const emailHtml = await render(template); - const appName = config.getRawPrivateConfig().branding?.app_name || "Pangolin"; + const appName = process.env.BRANDING_APP_NAME || "Pangolin"; // From the private config loading into env vars to seperate away the private config await emailClient.sendMail({ from: { diff --git a/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx b/server/emails/templates/NotifyUsageLimitApproaching.tsx similarity index 86% rename from server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx rename to server/emails/templates/NotifyUsageLimitApproaching.tsx index c66265e5..beab0300 100644 --- a/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx +++ b/server/emails/templates/NotifyUsageLimitApproaching.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import React from "react"; import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; import { themeColors } from "./lib/theme"; diff --git a/server/emails/templates/PrivateNotifyUsageLimitReached.tsx b/server/emails/templates/NotifyUsageLimitReached.tsx similarity index 87% rename from server/emails/templates/PrivateNotifyUsageLimitReached.tsx rename to server/emails/templates/NotifyUsageLimitReached.tsx index c4eac322..783d1b0e 100644 --- a/server/emails/templates/PrivateNotifyUsageLimitReached.tsx +++ b/server/emails/templates/NotifyUsageLimitReached.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import React from "react"; import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; import { themeColors } from "./lib/theme"; diff --git a/server/index.ts b/server/index.ts index 2497e301..f4571422 100644 --- a/server/index.ts +++ b/server/index.ts @@ -12,6 +12,7 @@ import config from "@server/lib/config"; import { setHostMeta } from "@server/lib/hostMeta"; import { initTelemetryClient } from "./lib/telemetry.js"; import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager.js"; +import { initCleanup } from "#dynamic/cleanup"; async function startServers() { await setHostMeta(); @@ -42,6 +43,8 @@ async function startServers() { integrationServer = createIntegrationApiServer(); } + await initCleanup(); + return { apiServer, nextServer, diff --git a/server/integrationApiServer.ts b/server/integrationApiServer.ts index cbbea83f..3416004c 100644 --- a/server/integrationApiServer.ts +++ b/server/integrationApiServer.ts @@ -7,7 +7,7 @@ import { errorHandlerMiddleware, notFoundMiddleware, } from "@server/middlewares"; -import { authenticated, unauthenticated } from "@server/routers/integration"; +import { authenticated, unauthenticated } from "#dynamic/routers/integration"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; import helmet from "helmet"; import swaggerUi from "swagger-ui-express"; diff --git a/server/internalServer.ts b/server/internalServer.ts index 0ba64eec..d15e3c45 100644 --- a/server/internalServer.ts +++ b/server/internalServer.ts @@ -8,7 +8,7 @@ import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares"; -import internal from "@server/routers/internal"; +import { internalRouter } from "#dynamic/routers/internal"; import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; const internalPort = config.getRawConfig().server.internal_port; @@ -23,7 +23,7 @@ export function createInternalServer() { internalServer.use(express.json()); const prefix = `/api/v1`; - internalServer.use(prefix, internal); + internalServer.use(prefix, internalRouter); internalServer.use(notFoundMiddleware); internalServer.use(errorHandlerMiddleware); diff --git a/server/lib/billing/createCustomer.ts b/server/lib/billing/createCustomer.ts new file mode 100644 index 00000000..7f65bfb2 --- /dev/null +++ b/server/lib/billing/createCustomer.ts @@ -0,0 +1,6 @@ +export async function createCustomer( + orgId: string, + email: string | null | undefined +): Promise { + return; +} diff --git a/server/lib/private/billing/features.ts b/server/lib/billing/features.ts similarity index 88% rename from server/lib/private/billing/features.ts rename to server/lib/billing/features.ts index 11d78bbb..b72543cc 100644 --- a/server/lib/private/billing/features.ts +++ b/server/lib/billing/features.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import Stripe from "stripe"; export enum FeatureId { diff --git a/server/lib/billing/getOrgTierData.ts b/server/lib/billing/getOrgTierData.ts new file mode 100644 index 00000000..24664790 --- /dev/null +++ b/server/lib/billing/getOrgTierData.ts @@ -0,0 +1,8 @@ +export async function getOrgTierData( + orgId: string +): Promise<{ tier: string | null; active: boolean }> { + let tier = null; + let active = false; + + return { tier, active }; +} diff --git a/server/lib/billing/index.ts b/server/lib/billing/index.ts new file mode 100644 index 00000000..6c3ef792 --- /dev/null +++ b/server/lib/billing/index.ts @@ -0,0 +1,5 @@ +export * from "./limitSet"; +export * from "./features"; +export * from "./limitsService"; +export * from "./getOrgTierData"; +export * from "./createCustomer"; \ No newline at end of file diff --git a/server/lib/private/billing/limitSet.ts b/server/lib/billing/limitSet.ts similarity index 82% rename from server/lib/private/billing/limitSet.ts rename to server/lib/billing/limitSet.ts index ec6107b2..153d8ae8 100644 --- a/server/lib/private/billing/limitSet.ts +++ b/server/lib/billing/limitSet.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { FeatureId } from "./features"; export type LimitSet = { diff --git a/server/lib/private/billing/limitsService.ts b/server/lib/billing/limitsService.ts similarity index 76% rename from server/lib/private/billing/limitsService.ts rename to server/lib/billing/limitsService.ts index 168f5580..a07f70b3 100644 --- a/server/lib/private/billing/limitsService.ts +++ b/server/lib/billing/limitsService.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { db, limits } from "@server/db"; import { and, eq } from "drizzle-orm"; import { LimitSet } from "./limitSet"; diff --git a/server/lib/private/billing/tiers.ts b/server/lib/billing/tiers.ts similarity index 69% rename from server/lib/private/billing/tiers.ts rename to server/lib/billing/tiers.ts index e6322c9f..6ccf8898 100644 --- a/server/lib/private/billing/tiers.ts +++ b/server/lib/billing/tiers.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - export enum TierId { STANDARD = "standard", } diff --git a/server/lib/private/billing/usageService.ts b/server/lib/billing/usageService.ts similarity index 97% rename from server/lib/private/billing/usageService.ts rename to server/lib/billing/usageService.ts index f18542d2..edff41f0 100644 --- a/server/lib/private/billing/usageService.ts +++ b/server/lib/billing/usageService.ts @@ -1,21 +1,7 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { eq, sql, and } from "drizzle-orm"; import NodeCache from "node-cache"; import { v4 as uuidv4 } from "uuid"; import { PutObjectCommand } from "@aws-sdk/client-s3"; -import { s3Client } from "../s3"; import * as fs from "fs/promises"; import * as path from "path"; import { @@ -30,10 +16,10 @@ import { Transaction } from "@server/db"; import { FeatureId, getFeatureMeterId } from "./features"; -import config from "@server/lib/config"; import logger from "@server/logger"; -import { sendToClient } from "@server/routers/ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { build } from "@server/build"; +import { s3Client } from "@server/lib/s3"; interface StripeEvent { identifier?: string; @@ -58,8 +44,10 @@ export class UsageService { if (build !== "saas") { return; } - this.bucketName = config.getRawPrivateConfig().stripe?.s3Bucket; - this.eventsDir = config.getRawPrivateConfig().stripe?.localFilePath; + // this.bucketName = privateConfig.getRawPrivateConfig().stripe?.s3Bucket; + // this.eventsDir = privateConfig.getRawPrivateConfig().stripe?.localFilePath; + this.bucketName = process.env.S3_BUCKET || undefined; + this.eventsDir = process.env.LOCAL_FILE_PATH || undefined; // Ensure events directory exists this.initializeEventsDirectory().then(() => { diff --git a/server/lib/blueprints/applyNewtDockerBlueprint.ts b/server/lib/blueprints/applyNewtDockerBlueprint.ts index f69e4854..2afba84c 100644 --- a/server/lib/blueprints/applyNewtDockerBlueprint.ts +++ b/server/lib/blueprints/applyNewtDockerBlueprint.ts @@ -1,4 +1,4 @@ -import { sendToClient } from "@server/routers/ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { processContainerLabels } from "./parseDockerContainers"; import { applyBlueprint } from "./applyBlueprint"; import { db, sites } from "@server/db"; diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index 407c6019..a31cfb9d 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -25,7 +25,7 @@ import { TargetData } from "./types"; import logger from "@server/logger"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { pickPort } from "@server/routers/target/helpers"; import { resourcePassword } from "@server/db"; import { hashPassword } from "@server/auth/password"; diff --git a/server/lib/remoteCertificates/certificates.ts b/server/lib/certificates.ts similarity index 96% rename from server/lib/remoteCertificates/certificates.ts rename to server/lib/certificates.ts index 6404ee75..4032d1ec 100644 --- a/server/lib/remoteCertificates/certificates.ts +++ b/server/lib/certificates.ts @@ -1,7 +1,7 @@ import axios from "axios"; -import { tokenManager } from "../tokenManager"; +import { tokenManager } from "./tokenManager"; import logger from "@server/logger"; -import config from "../config"; +import config from "./config"; /** * Get valid certificates for the specified domains diff --git a/server/lib/config.ts b/server/lib/config.ts index 8b084e62..103ea5ae 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -6,16 +6,10 @@ import { eq } from "drizzle-orm"; import { license } from "@server/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; import { fromError } from "zod-validation-error"; -import { - privateConfigSchema, - readPrivateConfigFile -} from "@server/lib/private/readConfigFile"; -import logger from "@server/logger"; import { build } from "@server/build"; export class Config { private rawConfig!: z.infer; - private rawPrivateConfig!: z.infer; supporterData: SupporterKey | null = null; @@ -37,19 +31,6 @@ export class Config { throw new Error(`Invalid configuration file: ${errors}`); } - const privateEnvironment = readPrivateConfigFile(); - - const { - data: parsedPrivateConfig, - success: privateSuccess, - error: privateError - } = privateConfigSchema.safeParse(privateEnvironment); - - if (!privateSuccess) { - const errors = fromError(privateError); - throw new Error(`Invalid private configuration file: ${errors}`); - } - if ( // @ts-ignore parsedConfig.users || @@ -109,110 +90,11 @@ export class Config { ? "true" : "false"; - if (parsedPrivateConfig.branding?.colors) { - process.env.BRANDING_COLORS = JSON.stringify( - parsedPrivateConfig.branding?.colors - ); - } - - if (parsedPrivateConfig.branding?.logo?.light_path) { - process.env.BRANDING_LOGO_LIGHT_PATH = - parsedPrivateConfig.branding?.logo?.light_path; - } - if (parsedPrivateConfig.branding?.logo?.dark_path) { - process.env.BRANDING_LOGO_DARK_PATH = - parsedPrivateConfig.branding?.logo?.dark_path || undefined; - } - - process.env.HIDE_SUPPORTER_KEY = parsedPrivateConfig.flags - ?.hide_supporter_key - ? "true" - : "false"; - - if (build != "oss") { - if (parsedPrivateConfig.branding?.logo?.light_path) { - process.env.BRANDING_LOGO_LIGHT_PATH = - parsedPrivateConfig.branding?.logo?.light_path; - } - if (parsedPrivateConfig.branding?.logo?.dark_path) { - process.env.BRANDING_LOGO_DARK_PATH = - parsedPrivateConfig.branding?.logo?.dark_path || undefined; - } - - process.env.BRANDING_LOGO_AUTH_WIDTH = parsedPrivateConfig.branding - ?.logo?.auth_page?.width - ? parsedPrivateConfig.branding?.logo?.auth_page?.width.toString() - : undefined; - process.env.BRANDING_LOGO_AUTH_HEIGHT = parsedPrivateConfig.branding - ?.logo?.auth_page?.height - ? parsedPrivateConfig.branding?.logo?.auth_page?.height.toString() - : undefined; - - process.env.BRANDING_LOGO_NAVBAR_WIDTH = parsedPrivateConfig - .branding?.logo?.navbar?.width - ? parsedPrivateConfig.branding?.logo?.navbar?.width.toString() - : undefined; - process.env.BRANDING_LOGO_NAVBAR_HEIGHT = parsedPrivateConfig - .branding?.logo?.navbar?.height - ? parsedPrivateConfig.branding?.logo?.navbar?.height.toString() - : undefined; - - process.env.BRANDING_FAVICON_PATH = - parsedPrivateConfig.branding?.favicon_path; - - process.env.BRANDING_APP_NAME = - parsedPrivateConfig.branding?.app_name || "Pangolin"; - - if (parsedPrivateConfig.branding?.footer) { - process.env.BRANDING_FOOTER = JSON.stringify( - parsedPrivateConfig.branding?.footer - ); - } - - process.env.LOGIN_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.login_page?.title_text || ""; - process.env.LOGIN_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.login_page?.subtitle_text || ""; - - process.env.SIGNUP_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.signup_page?.title_text || ""; - process.env.SIGNUP_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.signup_page?.subtitle_text || ""; - - process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = - parsedPrivateConfig.branding?.resource_auth_page - ?.hide_powered_by === true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = - parsedPrivateConfig.branding?.resource_auth_page?.show_logo === - true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.resource_auth_page?.title_text || - ""; - process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.resource_auth_page - ?.subtitle_text || ""; - - if (parsedPrivateConfig.branding?.background_image_path) { - process.env.BACKGROUND_IMAGE_PATH = - parsedPrivateConfig.branding?.background_image_path; - } - - if (parsedPrivateConfig.server.reo_client_id) { - process.env.REO_CLIENT_ID = - parsedPrivateConfig.server.reo_client_id; - } - } - if (parsedConfig.server.maxmind_db_path) { process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path; } this.rawConfig = parsedConfig; - this.rawPrivateConfig = parsedPrivateConfig; } public async initServer() { @@ -231,7 +113,6 @@ export class Config { private async checkKeyStatus() { const licenseStatus = await license.check(); if ( - !this.rawPrivateConfig.flags?.hide_supporter_key && build != "oss" && !licenseStatus.isHostLicensed ) { @@ -243,10 +124,6 @@ export class Config { return this.rawConfig; } - public getRawPrivateConfig() { - return this.rawPrivateConfig; - } - public getNoReplyEmail(): string | undefined { return ( this.rawConfig.email?.no_reply || this.rawConfig.email?.smtp_user diff --git a/server/middlewares/private/corsWithLoginPage.ts b/server/lib/corsWithLoginPage.ts similarity index 88% rename from server/middlewares/private/corsWithLoginPage.ts rename to server/lib/corsWithLoginPage.ts index 95867fa1..43b26264 100644 --- a/server/middlewares/private/corsWithLoginPage.ts +++ b/server/lib/corsWithLoginPage.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { Request, Response, NextFunction } from "express"; import cors, { CorsOptions } from "cors"; import config from "@server/lib/config"; diff --git a/server/lib/private/createUserAccountOrg.ts b/server/lib/createUserAccountOrg.ts similarity index 89% rename from server/lib/private/createUserAccountOrg.ts rename to server/lib/createUserAccountOrg.ts index abde5ca7..8677d8e3 100644 --- a/server/lib/private/createUserAccountOrg.ts +++ b/server/lib/createUserAccountOrg.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { isValidCIDR } from "@server/lib/validators"; import { getNextAvailableOrgSubnet } from "@server/lib/ip"; import { @@ -28,9 +15,9 @@ import { } from "@server/db"; import { eq } from "drizzle-orm"; import { defaultRoleAllowedActions } from "@server/routers/role"; -import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/private/billing"; -import { createCustomer } from "@server/routers/private/billing/createCustomer"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/billing"; +import { createCustomer } from "@server/private/lib/billing/createCustomer"; +import { usageService } from "@server/lib/billing/usageService"; export async function createUserAccountOrg( userId: string, diff --git a/server/lib/exitNodes/exitNodes.ts b/server/lib/exitNodes/exitNodes.ts index 8372d675..bb269710 100644 --- a/server/lib/exitNodes/exitNodes.ts +++ b/server/lib/exitNodes/exitNodes.ts @@ -16,7 +16,11 @@ export async function verifyExitNodeOrgAccess( return { hasAccess: true, exitNode }; } -export async function listExitNodes(orgId: string, filterOnline = false, noCloud = false) { +export async function listExitNodes( + orgId: string, + filterOnline = false, + noCloud = false +) { // TODO: pick which nodes to send and ping better than just all of them that are not remote const allExitNodes = await db .select({ @@ -59,7 +63,16 @@ export async function checkExitNodeOrg(exitNodeId: number, orgId: string) { return false; } -export async function resolveExitNodes(hostname: string, publicKey: string) { +export async function resolveExitNodes( + hostname: string, + publicKey: string +): Promise< + { + endpoint: string; + publicKey: string; + orgId: string; + }[] +> { // OSS version: simple implementation that returns empty array return []; } diff --git a/server/lib/exitNodes/index.ts b/server/lib/exitNodes/index.ts index dda94368..ba30ccc2 100644 --- a/server/lib/exitNodes/index.ts +++ b/server/lib/exitNodes/index.ts @@ -1,33 +1,4 @@ -import { build } from "@server/build"; - -// Import both modules -import * as exitNodesModule from "./exitNodes"; -import * as privateExitNodesModule from "./privateExitNodes"; - -// Conditionally export exit nodes implementation based on build type -const exitNodesImplementation = build === "oss" ? exitNodesModule : privateExitNodesModule; - -// Re-export all items from the selected implementation -export const { - verifyExitNodeOrgAccess, - listExitNodes, - selectBestExitNode, - checkExitNodeOrg, - resolveExitNodes -} = exitNodesImplementation; - -// Import communications modules -import * as exitNodeCommsModule from "./exitNodeComms"; -import * as privateExitNodeCommsModule from "./privateExitNodeComms"; - -// Conditionally export communications implementation based on build type -const exitNodeCommsImplementation = build === "oss" ? exitNodeCommsModule : privateExitNodeCommsModule; - -// Re-export communications functions from the selected implementation -export const { - sendToExitNode -} = exitNodeCommsImplementation; - -// Re-export shared modules +export * from "./exitNodes"; +export * from "./exitNodeComms"; export * from "./subnet"; export * from "./getCurrentExitNodeId"; \ No newline at end of file diff --git a/server/lib/private/rateLimitStore.ts b/server/lib/private/rateLimitStore.ts deleted file mode 100644 index 4700ba09..00000000 --- a/server/lib/private/rateLimitStore.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import RedisStore from "@server/db/private/redisStore"; -import { MemoryStore, Store } from "express-rate-limit"; - -export function createStore(): Store { - const rateLimitStore: Store = new RedisStore({ - prefix: 'api-rate-limit', // Optional: customize Redis key prefix - skipFailedRequests: true, // Don't count failed requests - skipSuccessfulRequests: false, // Count successful requests - }); - - return rateLimitStore; -} diff --git a/server/lib/private/s3.ts b/server/lib/private/s3.ts deleted file mode 100644 index 26b1d49b..00000000 --- a/server/lib/private/s3.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { S3Client } from "@aws-sdk/client-s3"; -import config from "@server/lib/config"; - -export const s3Client = new S3Client({ - region: config.getRawPrivateConfig().stripe?.s3Region || "us-east-1", -}); diff --git a/server/lib/remoteCertificates/index.ts b/server/lib/remoteCertificates/index.ts deleted file mode 100644 index fcd43d30..00000000 --- a/server/lib/remoteCertificates/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { build } from "@server/build"; - -// Import both modules -import * as certificateModule from "./certificates"; -import * as privateCertificateModule from "./privateCertificates"; - -// Conditionally export Remote Certificates implementation based on build type -const remoteCertificatesImplementation = build === "oss" ? certificateModule : privateCertificateModule; - -// Re-export all items from the selected implementation -export const { - getValidCertificatesForDomains, - getValidCertificatesForDomainsHybrid - } = remoteCertificatesImplementation; \ No newline at end of file diff --git a/server/lib/resend.ts b/server/lib/resend.ts new file mode 100644 index 00000000..931dca20 --- /dev/null +++ b/server/lib/resend.ts @@ -0,0 +1,15 @@ +export enum AudienceIds { + General = "", + Subscribed = "", + Churned = "" +} + +let resend; +export default resend; + +export async function moveEmailToAudience( + email: string, + audienceId: AudienceIds +) { + return +} \ No newline at end of file diff --git a/server/lib/s3.ts b/server/lib/s3.ts new file mode 100644 index 00000000..5fc3318f --- /dev/null +++ b/server/lib/s3.ts @@ -0,0 +1,5 @@ +import { S3Client } from "@aws-sdk/client-s3"; + +export const s3Client = new S3Client({ + region: process.env.S3_REGION || "us-east-1", +}); diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 51466ecf..435a749f 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -8,12 +8,12 @@ import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; import { tokenManager } from "../tokenManager"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; -import { getTraefikConfig } from "./"; +import { getTraefikConfig } from "#dynamic/lib/traefik"; import { getValidCertificatesForDomains, getValidCertificatesForDomainsHybrid -} from "../remoteCertificates"; -import { sendToExitNode } from "../exitNodes"; +} from "#dynamic/lib/certificates"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; export class TraefikConfigManager { diff --git a/server/lib/traefik/index.ts b/server/lib/traefik/index.ts index 1d654510..5630028c 100644 --- a/server/lib/traefik/index.ts +++ b/server/lib/traefik/index.ts @@ -1,11 +1 @@ -import { build } from "@server/build"; - -// Import both modules -import * as traefikModule from "./getTraefikConfig"; -import * as privateTraefikModule from "./privateGetTraefikConfig"; - -// Conditionally export Traefik configuration implementation based on build type -const traefikImplementation = build === "oss" ? traefikModule : privateTraefikModule; - -// Re-export all items from the selected implementation -export const { getTraefikConfig } = traefikImplementation; \ No newline at end of file +export * from "./getTraefikConfig"; \ No newline at end of file diff --git a/server/auth/sessions/privateRemoteExitNode.ts b/server/private/auth/sessions/remoteExitNode.ts similarity index 100% rename from server/auth/sessions/privateRemoteExitNode.ts rename to server/private/auth/sessions/remoteExitNode.ts diff --git a/server/private/cleanup.ts b/server/private/cleanup.ts new file mode 100644 index 00000000..8e57ce29 --- /dev/null +++ b/server/private/cleanup.ts @@ -0,0 +1,15 @@ +import { rateLimitService } from "#private/lib/rateLimit"; +import { cleanup as wsCleanup } from "#private/routers/ws"; + +async function cleanup() { + await rateLimitService.cleanup(); + await wsCleanup(); + + process.exit(0); +} + +export async function initCleanup() { + // Handle process termination + process.on("SIGTERM", () => cleanup()); + process.on("SIGINT", () => cleanup()); +} \ No newline at end of file diff --git a/server/routers/private/billing/createCustomer.ts b/server/private/lib/billing/createCustomer.ts similarity index 96% rename from server/routers/private/billing/createCustomer.ts rename to server/private/lib/billing/createCustomer.ts index d1c08a0e..52c72c53 100644 --- a/server/routers/private/billing/createCustomer.ts +++ b/server/private/lib/billing/createCustomer.ts @@ -13,7 +13,7 @@ import { customers, db } from "@server/db"; import { eq } from "drizzle-orm"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; import { build } from "@server/build"; export async function createCustomer( diff --git a/server/private/lib/billing/getOrgTierData.ts b/server/private/lib/billing/getOrgTierData.ts new file mode 100644 index 00000000..fbfb5cb0 --- /dev/null +++ b/server/private/lib/billing/getOrgTierData.ts @@ -0,0 +1,46 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { getTierPriceSet } from "@server/lib/billing/tiers"; +import { getOrgSubscriptionData } from "#private/routers/billing/getOrgSubscription"; +import { build } from "@server/build"; + +export async function getOrgTierData( + orgId: string +): Promise<{ tier: string | null; active: boolean }> { + let tier = null; + let active = false; + + if (build !== "saas") { + return { tier, active }; + } + + const { subscription, items } = await getOrgSubscriptionData(orgId); + + if (items && items.length > 0) { + const tierPriceSet = getTierPriceSet(); + // Iterate through tiers in order (earlier keys are higher tiers) + for (const [tierId, priceId] of Object.entries(tierPriceSet)) { + // Check if any subscription item matches this tier's price ID + const matchingItem = items.find((item) => item.priceId === priceId); + if (matchingItem) { + tier = tierId; + break; + } + } + } + if (subscription && subscription.status === "active") { + active = true; + } + return { tier, active }; +} diff --git a/server/lib/private/billing/index.ts b/server/private/lib/billing/index.ts similarity index 81% rename from server/lib/private/billing/index.ts rename to server/private/lib/billing/index.ts index 0212ee1c..13ca3761 100644 --- a/server/lib/private/billing/index.ts +++ b/server/private/lib/billing/index.ts @@ -11,6 +11,5 @@ * This file is not licensed under the AGPLv3. */ -export * from "./limitSet"; -export * from "./features"; -export * from "./limitsService"; +export * from "./getOrgTierData"; +export * from "./createCustomer"; \ No newline at end of file diff --git a/server/lib/remoteCertificates/privateCertificates.ts b/server/private/lib/certificates.ts similarity index 97% rename from server/lib/remoteCertificates/privateCertificates.ts rename to server/private/lib/certificates.ts index fabc9ea5..0924daf7 100644 --- a/server/lib/remoteCertificates/privateCertificates.ts +++ b/server/private/lib/certificates.ts @@ -11,10 +11,10 @@ * This file is not licensed under the AGPLv3. */ -import config from "../config"; +import config from "./config"; import { certificates, db } from "@server/db"; import { and, eq, isNotNull } from "drizzle-orm"; -import { decryptData } from "../encryption"; +import { decryptData } from "@server/lib/encryption"; import * as fs from "fs"; export async function getValidCertificatesForDomains( diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts new file mode 100644 index 00000000..a0dbf9a3 --- /dev/null +++ b/server/private/lib/config.ts @@ -0,0 +1,163 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { z } from "zod"; +import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; +import { db } from "@server/db"; +import { SupporterKey, supporterKey } from "@server/db"; +import { eq } from "drizzle-orm"; +import { license } from "@server/license/license"; +import { fromError } from "zod-validation-error"; +import { + privateConfigSchema, + readPrivateConfigFile +} from "#private/lib/readConfigFile"; +import { build } from "@server/build"; + +export class PrivateConfig { + private rawPrivateConfig!: z.infer; + + supporterData: SupporterKey | null = null; + + supporterHiddenUntil: number | null = null; + + isDev: boolean = process.env.ENVIRONMENT !== "prod"; + + constructor() { + const privateEnvironment = readPrivateConfigFile(); + + const { + data: parsedPrivateConfig, + success: privateSuccess, + error: privateError + } = privateConfigSchema.safeParse(privateEnvironment); + + if (!privateSuccess) { + const errors = fromError(privateError); + throw new Error(`Invalid private configuration file: ${errors}`); + } + + if (parsedPrivateConfig.branding?.colors) { + process.env.BRANDING_COLORS = JSON.stringify( + parsedPrivateConfig.branding?.colors + ); + } + + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + if (build != "oss") { + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + process.env.BRANDING_LOGO_AUTH_WIDTH = parsedPrivateConfig.branding + ?.logo?.auth_page?.width + ? parsedPrivateConfig.branding?.logo?.auth_page?.width.toString() + : undefined; + process.env.BRANDING_LOGO_AUTH_HEIGHT = parsedPrivateConfig.branding + ?.logo?.auth_page?.height + ? parsedPrivateConfig.branding?.logo?.auth_page?.height.toString() + : undefined; + + process.env.BRANDING_LOGO_NAVBAR_WIDTH = parsedPrivateConfig + .branding?.logo?.navbar?.width + ? parsedPrivateConfig.branding?.logo?.navbar?.width.toString() + : undefined; + process.env.BRANDING_LOGO_NAVBAR_HEIGHT = parsedPrivateConfig + .branding?.logo?.navbar?.height + ? parsedPrivateConfig.branding?.logo?.navbar?.height.toString() + : undefined; + + process.env.BRANDING_FAVICON_PATH = + parsedPrivateConfig.branding?.favicon_path; + + process.env.BRANDING_APP_NAME = + parsedPrivateConfig.branding?.app_name || "Pangolin"; + + if (parsedPrivateConfig.branding?.footer) { + process.env.BRANDING_FOOTER = JSON.stringify( + parsedPrivateConfig.branding?.footer + ); + } + + process.env.LOGIN_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.title_text || ""; + process.env.LOGIN_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.subtitle_text || ""; + + process.env.SIGNUP_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.title_text || ""; + process.env.SIGNUP_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.subtitle_text || ""; + + process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = + parsedPrivateConfig.branding?.resource_auth_page + ?.hide_powered_by === true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = + parsedPrivateConfig.branding?.resource_auth_page?.show_logo === + true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page?.title_text || + ""; + process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page + ?.subtitle_text || ""; + + if (parsedPrivateConfig.branding?.background_image_path) { + process.env.BACKGROUND_IMAGE_PATH = + parsedPrivateConfig.branding?.background_image_path; + } + + if (parsedPrivateConfig.server.reo_client_id) { + process.env.REO_CLIENT_ID = + parsedPrivateConfig.server.reo_client_id; + } + + if (parsedPrivateConfig.stripe?.s3Bucket) { + process.env.S3_BUCKET = parsedPrivateConfig.stripe.s3Bucket; + } + if (parsedPrivateConfig.stripe?.localFilePath) { + process.env.LOCAL_FILE_PATH = parsedPrivateConfig.stripe.localFilePath; + } + if (parsedPrivateConfig.stripe?.s3Region) { + process.env.S3_REGION = parsedPrivateConfig.stripe.s3Region; + } + } + + this.rawPrivateConfig = parsedPrivateConfig; + } + + public getRawPrivateConfig() { + return this.rawPrivateConfig; + } +} + +export const privateConfig = new PrivateConfig(); + +export default privateConfig; diff --git a/server/lib/exitNodes/privateExitNodeComms.ts b/server/private/lib/exitNodes/exitNodeComms.ts similarity index 95% rename from server/lib/exitNodes/privateExitNodeComms.ts rename to server/private/lib/exitNodes/exitNodeComms.ts index 163a962f..4cf8fbe7 100644 --- a/server/lib/exitNodes/privateExitNodeComms.ts +++ b/server/private/lib/exitNodes/exitNodeComms.ts @@ -15,8 +15,9 @@ import axios from "axios"; import logger from "@server/logger"; import { db, ExitNode, remoteExitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../../routers/ws"; -import { config } from "../config"; +import { sendToClient } from "#private/routers/ws"; +import privateConfig from "#private/lib/config"; +import config from "@server/lib/config"; interface ExitNodeRequest { remoteType?: string; @@ -65,7 +66,7 @@ export async function sendToExitNode( logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); if (exitNode.name == config.getRawConfig().gerbil.exit_node_name) { - hostname = config.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; + hostname = privateConfig.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; } if (!hostname) { diff --git a/server/lib/exitNodes/privateExitNodes.ts b/server/private/lib/exitNodes/exitNodes.ts similarity index 100% rename from server/lib/exitNodes/privateExitNodes.ts rename to server/private/lib/exitNodes/exitNodes.ts diff --git a/server/private/lib/exitNodes/index.ts b/server/private/lib/exitNodes/index.ts new file mode 100644 index 00000000..098a0580 --- /dev/null +++ b/server/private/lib/exitNodes/index.ts @@ -0,0 +1,15 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./exitNodeComms"; +export * from "./exitNodes"; \ No newline at end of file diff --git a/server/db/private/rateLimit.test.ts b/server/private/lib/rateLimit.test.ts similarity index 100% rename from server/db/private/rateLimit.test.ts rename to server/private/lib/rateLimit.test.ts diff --git a/server/db/private/rateLimit.ts b/server/private/lib/rateLimit.ts similarity index 98% rename from server/db/private/rateLimit.ts rename to server/private/lib/rateLimit.ts index ff8589bc..1d0e62ae 100644 --- a/server/db/private/rateLimit.ts +++ b/server/private/lib/rateLimit.ts @@ -12,7 +12,7 @@ */ import logger from "@server/logger"; -import redisManager from "@server/db/private/redis"; +import redisManager from "@server/private/lib/redis"; import { build } from "@server/build"; // Rate limiting configuration @@ -451,8 +451,4 @@ export class RateLimitService { } // Export singleton instance -export const rateLimitService = new RateLimitService(); - -// Handle process termination -process.on("SIGTERM", () => rateLimitService.cleanup()); -process.on("SIGINT", () => rateLimitService.cleanup()); \ No newline at end of file +export const rateLimitService = new RateLimitService(); \ No newline at end of file diff --git a/server/private/lib/rateLimitStore.ts b/server/private/lib/rateLimitStore.ts new file mode 100644 index 00000000..11e1a9d6 --- /dev/null +++ b/server/private/lib/rateLimitStore.ts @@ -0,0 +1,32 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { build } from "@server/build"; +import privateConfig from "#private/lib/config"; +import { MemoryStore, Store } from "express-rate-limit"; +import RedisStore from "#private/lib/redisStore"; + +export function createStore(): Store { + if (build != "oss" && privateConfig.getRawPrivateConfig().flags?.enable_redis) { + const rateLimitStore: Store = new RedisStore({ + prefix: "api-rate-limit", // Optional: customize Redis key prefix + skipFailedRequests: true, // Don't count failed requests + skipSuccessfulRequests: false // Count successful requests + }); + + return rateLimitStore; + } else { + const rateLimitStore: Store = new MemoryStore(); + return rateLimitStore; + } +} diff --git a/server/lib/private/readConfigFile.ts b/server/private/lib/readConfigFile.ts similarity index 99% rename from server/lib/private/readConfigFile.ts rename to server/private/lib/readConfigFile.ts index ddc9ee82..c1847ba5 100644 --- a/server/lib/private/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -74,7 +74,6 @@ export const privateConfigSchema = z flags: z .object({ enable_redis: z.boolean().optional(), - hide_supporter_key: z.boolean().optional() }) .optional(), branding: z diff --git a/server/db/private/redis.ts b/server/private/lib/redis.ts similarity index 98% rename from server/db/private/redis.ts rename to server/private/lib/redis.ts index d6e67262..e74874f2 100644 --- a/server/db/private/redis.ts +++ b/server/private/lib/redis.ts @@ -13,7 +13,7 @@ import Redis, { RedisOptions } from "ioredis"; import logger from "@server/logger"; -import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import { build } from "@server/build"; class RedisManager { @@ -46,7 +46,7 @@ class RedisManager { this.isEnabled = false; return; } - this.isEnabled = config.getRawPrivateConfig().flags?.enable_redis || false; + this.isEnabled = privateConfig.getRawPrivateConfig().flags?.enable_redis || false; if (this.isEnabled) { this.initializeClients(); } @@ -93,7 +93,7 @@ class RedisManager { } private getRedisConfig(): RedisOptions { - const redisConfig = config.getRawPrivateConfig().redis!; + const redisConfig = privateConfig.getRawPrivateConfig().redis!; const opts: RedisOptions = { host: redisConfig.host!, port: redisConfig.port!, @@ -108,7 +108,7 @@ class RedisManager { } private getReplicaRedisConfig(): RedisOptions | null { - const redisConfig = config.getRawPrivateConfig().redis!; + const redisConfig = privateConfig.getRawPrivateConfig().redis!; if (!redisConfig.replicas || redisConfig.replicas.length === 0) { return null; } diff --git a/server/db/private/redisStore.ts b/server/private/lib/redisStore.ts similarity index 100% rename from server/db/private/redisStore.ts rename to server/private/lib/redisStore.ts diff --git a/server/lib/private/resend.ts b/server/private/lib/resend.ts similarity index 96% rename from server/lib/private/resend.ts rename to server/private/lib/resend.ts index 26c3f4a6..1aac3d07 100644 --- a/server/lib/private/resend.ts +++ b/server/private/lib/resend.ts @@ -12,7 +12,7 @@ */ import { Resend } from "resend"; -import config from "../config"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; export enum AudienceIds { @@ -22,7 +22,7 @@ export enum AudienceIds { } const resend = new Resend( - config.getRawPrivateConfig().server.resend_api_key || "missing" + privateConfig.getRawPrivateConfig().server.resend_api_key || "missing" ); export default resend; diff --git a/server/lib/private/stripe.ts b/server/private/lib/stripe.ts similarity index 84% rename from server/lib/private/stripe.ts rename to server/private/lib/stripe.ts index 1170202d..477934b4 100644 --- a/server/lib/private/stripe.ts +++ b/server/private/lib/stripe.ts @@ -12,13 +12,13 @@ */ import Stripe from "stripe"; -import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; import { build } from "@server/build"; let stripe: Stripe | undefined = undefined; if (build == "saas") { - const stripeApiKey = config.getRawPrivateConfig().stripe?.secret_key; + const stripeApiKey = privateConfig.getRawPrivateConfig().stripe?.secret_key; if (!stripeApiKey) { logger.error("Stripe secret key is not configured"); } diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts similarity index 99% rename from server/lib/traefik/privateGetTraefikConfig.ts rename to server/private/lib/traefik/getTraefikConfig.ts index 1350e8b7..161ef48c 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -25,7 +25,7 @@ import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; -import { sanitize } from "./utils"; +import { sanitize } from "@server/lib/traefik/utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; diff --git a/server/private/lib/traefik/index.ts b/server/private/lib/traefik/index.ts new file mode 100644 index 00000000..30d83181 --- /dev/null +++ b/server/private/lib/traefik/index.ts @@ -0,0 +1,14 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./getTraefikConfig"; \ No newline at end of file diff --git a/server/middlewares/private/index.ts b/server/private/middlewares/index.ts similarity index 92% rename from server/middlewares/private/index.ts rename to server/private/middlewares/index.ts index f034001d..c92b0d3d 100644 --- a/server/middlewares/private/index.ts +++ b/server/private/middlewares/index.ts @@ -15,4 +15,4 @@ export * from "./verifyCertificateAccess"; export * from "./verifyRemoteExitNodeAccess"; export * from "./verifyIdpAccess"; export * from "./verifyLoginPageAccess"; -export * from "./corsWithLoginPage"; \ No newline at end of file +export * from "../../lib/corsWithLoginPage"; \ No newline at end of file diff --git a/server/middlewares/private/verifyCertificateAccess.ts b/server/private/middlewares/verifyCertificateAccess.ts similarity index 100% rename from server/middlewares/private/verifyCertificateAccess.ts rename to server/private/middlewares/verifyCertificateAccess.ts diff --git a/server/middlewares/private/verifyIdpAccess.ts b/server/private/middlewares/verifyIdpAccess.ts similarity index 100% rename from server/middlewares/private/verifyIdpAccess.ts rename to server/private/middlewares/verifyIdpAccess.ts diff --git a/server/middlewares/private/verifyLoginPageAccess.ts b/server/private/middlewares/verifyLoginPageAccess.ts similarity index 100% rename from server/middlewares/private/verifyLoginPageAccess.ts rename to server/private/middlewares/verifyLoginPageAccess.ts diff --git a/server/middlewares/private/verifyRemoteExitNode.ts b/server/private/middlewares/verifyRemoteExitNode.ts similarity index 94% rename from server/middlewares/private/verifyRemoteExitNode.ts rename to server/private/middlewares/verifyRemoteExitNode.ts index 45c244e2..2f6d99d2 100644 --- a/server/middlewares/private/verifyRemoteExitNode.ts +++ b/server/private/middlewares/verifyRemoteExitNode.ts @@ -16,7 +16,7 @@ import ErrorResponse from "@server/types/ErrorResponse"; import config from "@server/lib/config"; import { unauthorized } from "@server/auth/unauthorizedResponse"; import logger from "@server/logger"; -import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; +import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remoteExitNode"; export const verifySessionRemoteExitNodeMiddleware = async ( req: any, diff --git a/server/middlewares/private/verifyRemoteExitNodeAccess.ts b/server/private/middlewares/verifyRemoteExitNodeAccess.ts similarity index 100% rename from server/middlewares/private/verifyRemoteExitNodeAccess.ts rename to server/private/middlewares/verifyRemoteExitNodeAccess.ts diff --git a/server/routers/auth/privateGetSessionTransferToken.ts b/server/private/routers/auth/getSessionTransferToken.ts similarity index 100% rename from server/routers/auth/privateGetSessionTransferToken.ts rename to server/private/routers/auth/getSessionTransferToken.ts diff --git a/server/private/routers/auth/index.ts b/server/private/routers/auth/index.ts new file mode 100644 index 00000000..39a60031 --- /dev/null +++ b/server/private/routers/auth/index.ts @@ -0,0 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./transferSession"; +export * from "./getSessionTransferToken"; +export * from "./quickStart"; \ No newline at end of file diff --git a/server/routers/auth/privateQuickStart.ts b/server/private/routers/auth/quickStart.ts similarity index 98% rename from server/routers/auth/privateQuickStart.ts rename to server/private/routers/auth/quickStart.ts index 683c24a8..582ac4d5 100644 --- a/server/routers/auth/privateQuickStart.ts +++ b/server/private/routers/auth/quickStart.ts @@ -50,16 +50,16 @@ import config from "@server/lib/config"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; import { UserType } from "@server/types/UserTypes"; -import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; +import { createUserAccountOrg } from "@server/lib/createUserAccountOrg"; import { sendEmail } from "@server/emails"; import WelcomeQuickStart from "@server/emails/templates/WelcomeQuickStart"; import { alphabet, generateRandomString } from "oslo/crypto"; import { createDate, TimeSpan } from "oslo"; import { getUniqueResourceName, getUniqueSiteName } from "@server/db/names"; -import { pickPort } from "../target/helpers"; -import { addTargets } from "../newt/targets"; +import { pickPort } from "@server/routers/target/helpers"; +import { addTargets } from "@server/routers/newt/targets"; import { isTargetValid } from "@server/lib/validators"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#private/lib/exitNodes"; const bodySchema = z.object({ email: z.string().toLowerCase().email(), diff --git a/server/routers/auth/privateTransferSession.ts b/server/private/routers/auth/transferSession.ts similarity index 100% rename from server/routers/auth/privateTransferSession.ts rename to server/private/routers/auth/transferSession.ts diff --git a/server/routers/private/billing/createCheckoutSession.ts b/server/private/routers/billing/createCheckoutSession.ts similarity index 95% rename from server/routers/private/billing/createCheckoutSession.ts rename to server/private/routers/billing/createCheckoutSession.ts index 67507b68..6e1e28c2 100644 --- a/server/routers/private/billing/createCheckoutSession.ts +++ b/server/private/routers/billing/createCheckoutSession.ts @@ -21,9 +21,9 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; -import stripe from "@server/lib/private/stripe"; -import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/private/billing"; -import { getTierPriceSet, TierId } from "@server/lib/private/billing/tiers"; +import stripe from "#private/lib/stripe"; +import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing"; +import { getTierPriceSet, TierId } from "@server/lib/billing/tiers"; const createCheckoutSessionSchema = z .object({ diff --git a/server/routers/private/billing/createPortalSession.ts b/server/private/routers/billing/createPortalSession.ts similarity index 98% rename from server/routers/private/billing/createPortalSession.ts rename to server/private/routers/billing/createPortalSession.ts index aa672377..eb55f007 100644 --- a/server/routers/private/billing/createPortalSession.ts +++ b/server/private/routers/billing/createPortalSession.ts @@ -21,7 +21,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; const createPortalSessionSchema = z .object({ diff --git a/server/routers/private/billing/getOrgSubscription.ts b/server/private/routers/billing/getOrgSubscription.ts similarity index 100% rename from server/routers/private/billing/getOrgSubscription.ts rename to server/private/routers/billing/getOrgSubscription.ts diff --git a/server/routers/private/billing/getOrgUsage.ts b/server/private/routers/billing/getOrgUsage.ts similarity index 96% rename from server/routers/private/billing/getOrgUsage.ts rename to server/private/routers/billing/getOrgUsage.ts index e1544e06..5aad9609 100644 --- a/server/routers/private/billing/getOrgUsage.ts +++ b/server/private/routers/billing/getOrgUsage.ts @@ -23,8 +23,8 @@ import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { Limit, limits, Usage, usage } from "@server/db"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const getOrgSchema = z .object({ diff --git a/server/routers/private/billing/hooks/handleCustomerCreated.ts b/server/private/routers/billing/hooks/handleCustomerCreated.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerCreated.ts rename to server/private/routers/billing/hooks/handleCustomerCreated.ts diff --git a/server/routers/private/billing/hooks/handleCustomerDeleted.ts b/server/private/routers/billing/hooks/handleCustomerDeleted.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerDeleted.ts rename to server/private/routers/billing/hooks/handleCustomerDeleted.ts diff --git a/server/routers/private/billing/hooks/handleCustomerUpdated.ts b/server/private/routers/billing/hooks/handleCustomerUpdated.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerUpdated.ts rename to server/private/routers/billing/hooks/handleCustomerUpdated.ts diff --git a/server/routers/private/billing/hooks/handleSubscriptionCreated.ts b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts similarity index 97% rename from server/routers/private/billing/hooks/handleSubscriptionCreated.ts rename to server/private/routers/billing/hooks/handleSubscriptionCreated.ts index ee6376c9..223a2545 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionCreated.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts @@ -22,9 +22,9 @@ import { } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; +import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; export async function handleSubscriptionCreated( subscription: Stripe.Subscription diff --git a/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts similarity index 97% rename from server/routers/private/billing/hooks/handleSubscriptionDeleted.ts rename to server/private/routers/billing/hooks/handleSubscriptionDeleted.ts index 95123731..114a4b30 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts @@ -16,7 +16,7 @@ import { subscriptions, db, subscriptionItems, customers, userOrgs, users } from import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; +import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; export async function handleSubscriptionDeleted( subscription: Stripe.Subscription diff --git a/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts b/server/private/routers/billing/hooks/handleSubscriptionUpdated.ts similarity index 98% rename from server/routers/private/billing/hooks/handleSubscriptionUpdated.ts rename to server/private/routers/billing/hooks/handleSubscriptionUpdated.ts index f1cbcafe..01086054 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionUpdated.ts @@ -23,8 +23,8 @@ import { } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; -import { getFeatureIdByMetricId } from "@server/lib/private/billing/features"; -import stripe from "@server/lib/private/stripe"; +import { getFeatureIdByMetricId } from "@server/lib/billing/features"; +import stripe from "#private/lib/stripe"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; export async function handleSubscriptionUpdated( diff --git a/server/routers/private/billing/index.ts b/server/private/routers/billing/index.ts similarity index 100% rename from server/routers/private/billing/index.ts rename to server/private/routers/billing/index.ts diff --git a/server/routers/private/billing/internalGetOrgTier.ts b/server/private/routers/billing/internalGetOrgTier.ts similarity index 68% rename from server/routers/private/billing/internalGetOrgTier.ts rename to server/private/routers/billing/internalGetOrgTier.ts index 7f8cc642..8db41807 100644 --- a/server/routers/private/billing/internalGetOrgTier.ts +++ b/server/private/routers/billing/internalGetOrgTier.ts @@ -18,9 +18,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; -import { getTierPriceSet } from "@server/lib/private/billing/tiers"; -import { getOrgSubscriptionData } from "./getOrgSubscription"; -import { build } from "@server/build"; +import { getOrgTierData } from "#private/lib/billing"; const getOrgSchema = z .object({ @@ -87,33 +85,3 @@ export async function getOrgTier( ); } } - -export async function getOrgTierData( - orgId: string -): Promise<{ tier: string | null; active: boolean }> { - let tier = null; - let active = false; - - if (build !== "saas") { - return { tier, active }; - } - - const { subscription, items } = await getOrgSubscriptionData(orgId); - - if (items && items.length > 0) { - const tierPriceSet = getTierPriceSet(); - // Iterate through tiers in order (earlier keys are higher tiers) - for (const [tierId, priceId] of Object.entries(tierPriceSet)) { - // Check if any subscription item matches this tier's price ID - const matchingItem = items.find((item) => item.priceId === priceId); - if (matchingItem) { - tier = tierId; - break; - } - } - } - if (subscription && subscription.status === "active") { - active = true; - } - return { tier, active }; -} diff --git a/server/routers/private/billing/subscriptionLifecycle.ts b/server/private/routers/billing/subscriptionLifecycle.ts similarity index 93% rename from server/routers/private/billing/subscriptionLifecycle.ts rename to server/private/routers/billing/subscriptionLifecycle.ts index 82dbfdbe..06b2a2a8 100644 --- a/server/routers/private/billing/subscriptionLifecycle.ts +++ b/server/private/routers/billing/subscriptionLifecycle.ts @@ -11,8 +11,8 @@ * This file is not licensed under the AGPLv3. */ -import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/private/billing"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; import logger from "@server/logger"; export async function handleSubscriptionLifesycle(orgId: string, status: string) { diff --git a/server/routers/private/billing/webhooks.ts b/server/private/routers/billing/webhooks.ts similarity index 95% rename from server/routers/private/billing/webhooks.ts rename to server/private/routers/billing/webhooks.ts index 2844943a..24ad1074 100644 --- a/server/routers/private/billing/webhooks.ts +++ b/server/private/routers/billing/webhooks.ts @@ -11,8 +11,8 @@ * This file is not licensed under the AGPLv3. */ -import stripe from "@server/lib/private/stripe"; -import config from "@server/lib/config"; +import stripe from "#private/lib/stripe"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; import createHttpError from "http-errors"; import { response } from "@server/lib/response"; @@ -26,13 +26,13 @@ import { handleCustomerUpdated } from "./hooks/handleCustomerUpdated"; import { handleSubscriptionDeleted } from "./hooks/handleSubscriptionDeleted"; import { handleCustomerDeleted } from "./hooks/handleCustomerDeleted"; -export async function stripeWebhookHandler( +export async function billingWebhookHandler( req: Request, res: Response, next: NextFunction ): Promise { let event: Stripe.Event = req.body; - const endpointSecret = config.getRawPrivateConfig().stripe?.webhook_secret; + const endpointSecret = privateConfig.getRawPrivateConfig().stripe?.webhook_secret; if (!endpointSecret) { logger.warn("Stripe webhook secret is not configured. Webhook events will not be priocessed."); return next( diff --git a/server/routers/private/certificates/createCertificate.ts b/server/private/routers/certificates/createCertificate.ts similarity index 100% rename from server/routers/private/certificates/createCertificate.ts rename to server/private/routers/certificates/createCertificate.ts diff --git a/server/routers/private/certificates/getCertificate.ts b/server/private/routers/certificates/getCertificate.ts similarity index 100% rename from server/routers/private/certificates/getCertificate.ts rename to server/private/routers/certificates/getCertificate.ts diff --git a/server/routers/private/certificates/index.ts b/server/private/routers/certificates/index.ts similarity index 100% rename from server/routers/private/certificates/index.ts rename to server/private/routers/certificates/index.ts diff --git a/server/routers/private/certificates/restartCertificate.ts b/server/private/routers/certificates/restartCertificate.ts similarity index 100% rename from server/routers/private/certificates/restartCertificate.ts rename to server/private/routers/certificates/restartCertificate.ts diff --git a/server/routers/domain/privateCheckDomainNamespaceAvailability.ts b/server/private/routers/domain/checkDomainNamespaceAvailability.ts similarity index 100% rename from server/routers/domain/privateCheckDomainNamespaceAvailability.ts rename to server/private/routers/domain/checkDomainNamespaceAvailability.ts diff --git a/server/private/routers/domain/index.ts b/server/private/routers/domain/index.ts new file mode 100644 index 00000000..da9cec3f --- /dev/null +++ b/server/private/routers/domain/index.ts @@ -0,0 +1,15 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./checkDomainNamespaceAvailability"; +export * from "./listDomainNamespaces"; \ No newline at end of file diff --git a/server/routers/domain/privateListDomainNamespaces.ts b/server/private/routers/domain/listDomainNamespaces.ts similarity index 100% rename from server/routers/domain/privateListDomainNamespaces.ts rename to server/private/routers/domain/listDomainNamespaces.ts diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts new file mode 100644 index 00000000..fac7c0c4 --- /dev/null +++ b/server/private/routers/external.ts @@ -0,0 +1,262 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as certificates from "#private/routers/certificates"; +import { createStore } from "#private/lib/rateLimitStore"; +import * as billing from "#private/routers/billing"; +import * as remoteExitNode from "#private/routers/remoteExitNode"; +import * as loginPage from "#private/routers/loginPage"; +import * as orgIdp from "#private/routers/orgIdp"; +import * as domain from "#private/routers/domain"; +import * as auth from "#private/routers/auth"; + +import { Router } from "express"; +import { verifyOrgAccess, verifySessionUserMiddleware, verifyUserHasAction } from "@server/middlewares"; +import { ActionsEnum } from "@server/auth/actions"; +import { + verifyCertificateAccess, + verifyIdpAccess, + verifyLoginPageAccess, + verifyRemoteExitNodeAccess +} from "#private/middlewares"; +import rateLimit, { ipKeyGenerator } from "express-rate-limit"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +import { unauthenticated as ua, authenticated as a } from "@server/routers/external"; + +export const authenticated = a; +export const unauthenticated = ua; + +unauthenticated.post( + "/quick-start", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + keyGenerator: (req) => req.path, + handler: (req, res, next) => { + const message = `We're too busy right now. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + auth.quickStart +); + +unauthenticated.post( + "/remote-exit-node/quick-start", + rateLimit({ + windowMs: 60 * 60 * 1000, + max: 5, + keyGenerator: (req) => `${req.path}:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only create 5 remote exit nodes every hour. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + remoteExitNode.quickStartRemoteExitNode +); + + +authenticated.put( + "/org/:orgId/idp/oidc", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createIdp), + orgIdp.createOrgOidcIdp +); + +authenticated.post( + "/org/:orgId/idp/:idpId/oidc", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.updateIdp), + orgIdp.updateOrgOidcIdp +); + +authenticated.delete( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.deleteIdp), + orgIdp.deleteOrgIdp +); + +authenticated.get( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.getIdp), + orgIdp.getOrgIdp +); + +authenticated.get( + "/org/:orgId/idp", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listIdps), + orgIdp.listOrgIdps +); + +authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids + +authenticated.get( + "/org/:orgId/certificate/:domainId/:domain", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.getCertificate), + certificates.getCertificate +); + +authenticated.post( + "/org/:orgId/certificate/:certId/restart", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.restartCertificate), + certificates.restartCertificate +); + +authenticated.post( + "/org/:orgId/billing/create-checkout-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createCheckoutSession +); + +authenticated.post( + "/org/:orgId/billing/create-portal-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createPortalSession +); + +authenticated.get( + "/org/:orgId/billing/subscription", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgSubscription +); + +authenticated.get( + "/org/:orgId/billing/usage", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgUsage +); + +authenticated.get("/domain/namespaces", domain.listDomainNamespaces); + +authenticated.get( + "/domain/check-namespace-availability", + domain.checkDomainNamespaceAvailability +); + +authenticated.put( + "/org/:orgId/remote-exit-node", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.createRemoteExitNode +); + +authenticated.get( + "/org/:orgId/remote-exit-nodes", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listRemoteExitNode), + remoteExitNode.listRemoteExitNodes +); + +authenticated.get( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.getRemoteExitNode), + remoteExitNode.getRemoteExitNode +); + +authenticated.get( + "/org/:orgId/pick-remote-exit-node-defaults", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.pickRemoteExitNodeDefaults +); + +authenticated.delete( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), + remoteExitNode.deleteRemoteExitNode +); + +authenticated.put( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createLoginPage), + loginPage.createLoginPage +); + +authenticated.post( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.updateLoginPage), + loginPage.updateLoginPage +); + +authenticated.delete( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.deleteLoginPage), + loginPage.deleteLoginPage +); + +authenticated.get( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.getLoginPage), + loginPage.getLoginPage +); + +export const authRouter = Router(); + +authRouter.post( + "/remoteExitNode/get-token", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 900, + keyGenerator: (req) => + `remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + remoteExitNode.getRemoteExitNodeToken +); + +authRouter.post( + "/transfer-session-token", + rateLimit({ + windowMs: 1 * 60 * 1000, + max: 60, + keyGenerator: (req) => + `transferSessionToken:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + auth.transferSession +); \ No newline at end of file diff --git a/server/routers/gerbil/privateCreateExitNode.ts b/server/private/routers/gerbil/createExitNode.ts similarity index 100% rename from server/routers/gerbil/privateCreateExitNode.ts rename to server/private/routers/gerbil/createExitNode.ts diff --git a/server/routers/gerbil/privateReceiveBandwidth.ts b/server/private/routers/gerbil/receiveBandwidth.ts similarity index 100% rename from server/routers/gerbil/privateReceiveBandwidth.ts rename to server/private/routers/gerbil/receiveBandwidth.ts diff --git a/server/routers/private/hybrid.ts b/server/private/routers/hybrid.ts similarity index 98% rename from server/routers/private/hybrid.ts rename to server/private/routers/hybrid.ts index 2b59aa40..6d817853 100644 --- a/server/routers/private/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -11,7 +11,7 @@ * This file is not licensed under the AGPLv3. */ -import { verifySessionRemoteExitNodeMiddleware } from "@server/middlewares/private/verifyRemoteExitNode"; +import { verifySessionRemoteExitNodeMiddleware } from "#private/middlewares/verifyRemoteExitNode"; import { Router } from "express"; import { db, @@ -55,21 +55,22 @@ import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; -import { getTraefikConfig } from "../../lib/traefik"; +import { getTraefikConfig } from "#private/lib/traefik"; import { generateGerbilConfig, generateRelayMappings, updateAndGenerateEndpointDestinations, updateSiteBandwidth -} from "../gerbil"; +} from "@server/routers/gerbil"; import * as gerbil from "@server/routers/gerbil"; import logger from "@server/logger"; import { decryptData } from "@server/lib/encryption"; -import { config } from "@server/lib/config"; +import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import * as fs from "fs"; -import { exchangeSession } from "../badger"; +import { exchangeSession } from "@server/routers/badger"; import { validateResourceSessionToken } from "@server/auth/sessions/resource"; -import { checkExitNodeOrg, resolveExitNodes } from "@server/lib/exitNodes"; +import { checkExitNodeOrg, resolveExitNodes } from "#private/lib/exitNodes"; import { maxmindLookup } from "@server/db/maxmind"; // Zod schemas for request validation @@ -211,7 +212,7 @@ export type UserSessionWithUser = { }; // Root routes -const hybridRouter = Router(); +export const hybridRouter = Router(); hybridRouter.use(verifySessionRemoteExitNodeMiddleware); hybridRouter.get( @@ -387,7 +388,7 @@ hybridRouter.get( } const encryptionKeyPath = - config.getRawPrivateConfig().server.encryption_key_path; + privateConfig.getRawPrivateConfig().server.encryption_key_path; if (!fs.existsSync(encryptionKeyPath)) { throw new Error( @@ -1488,6 +1489,4 @@ hybridRouter.post( ); } } -); - -export default hybridRouter; +); \ No newline at end of file diff --git a/server/private/routers/integration.ts b/server/private/routers/integration.ts new file mode 100644 index 00000000..d767424a --- /dev/null +++ b/server/private/routers/integration.ts @@ -0,0 +1,42 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as orgIdp from "#private/routers/orgIdp"; +import * as org from "#private/routers/org"; + +import { Router } from "express"; +import { + verifyApiKey, + verifyApiKeyHasAction, + verifyApiKeyIsRoot, +} from "@server/middlewares"; +import { ActionsEnum } from "@server/auth/actions"; + +import { unauthenticated as ua, authenticated as a } from "@server/routers/integration"; + +export const unauthenticated = ua; +export const authenticated = a; + +authenticated.post( + `/org/:orgId/send-usage-notification`, + verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine + verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), + org.sendUsageNotification +); + +authenticated.delete( + "/idp/:idpId", + verifyApiKeyIsRoot, + verifyApiKeyHasAction(ActionsEnum.deleteIdp), + orgIdp.deleteOrgIdp +); \ No newline at end of file diff --git a/server/private/routers/internal.ts b/server/private/routers/internal.ts new file mode 100644 index 00000000..ab3db1ce --- /dev/null +++ b/server/private/routers/internal.ts @@ -0,0 +1,36 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as loginPage from "#private/routers/loginPage"; +import * as auth from "#private/routers/auth"; +import * as orgIdp from "#private/routers/orgIdp"; +import * as billing from "#private/routers/billing"; + +import { Router } from "express"; +import { verifySessionUserMiddleware } from "@server/middlewares"; + +import { internalRouter as ir } from "@server/routers/internal"; + +export const internalRouter = ir; + +internalRouter.get("/org/:orgId/idp", orgIdp.listOrgIdps); + +internalRouter.get("/org/:orgId/billing/tier", billing.getOrgTier); + +internalRouter.get("/login-page", loginPage.loadLoginPage); + +internalRouter.post( + "/get-session-transfer-token", + verifySessionUserMiddleware, + auth.getSessionTransferToken +); diff --git a/server/routers/private/loginPage/createLoginPage.ts b/server/private/routers/loginPage/createLoginPage.ts similarity index 96% rename from server/routers/private/loginPage/createLoginPage.ts rename to server/private/routers/loginPage/createLoginPage.ts index fca29aae..9c0359fe 100644 --- a/server/routers/private/loginPage/createLoginPage.ts +++ b/server/private/routers/loginPage/createLoginPage.ts @@ -29,9 +29,9 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq, and } from "drizzle-orm"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { createCertificate } from "#private/routers/certificates/createCertificate"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; const paramsSchema = z diff --git a/server/routers/private/loginPage/deleteLoginPage.ts b/server/private/routers/loginPage/deleteLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/deleteLoginPage.ts rename to server/private/routers/loginPage/deleteLoginPage.ts diff --git a/server/routers/private/loginPage/getLoginPage.ts b/server/private/routers/loginPage/getLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/getLoginPage.ts rename to server/private/routers/loginPage/getLoginPage.ts diff --git a/server/routers/private/loginPage/index.ts b/server/private/routers/loginPage/index.ts similarity index 100% rename from server/routers/private/loginPage/index.ts rename to server/private/routers/loginPage/index.ts diff --git a/server/routers/private/loginPage/loadLoginPage.ts b/server/private/routers/loginPage/loadLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/loadLoginPage.ts rename to server/private/routers/loginPage/loadLoginPage.ts diff --git a/server/routers/private/loginPage/updateLoginPage.ts b/server/private/routers/loginPage/updateLoginPage.ts similarity index 96% rename from server/routers/private/loginPage/updateLoginPage.ts rename to server/private/routers/loginPage/updateLoginPage.ts index 9c19913d..4aebbb7a 100644 --- a/server/routers/private/loginPage/updateLoginPage.ts +++ b/server/private/routers/loginPage/updateLoginPage.ts @@ -22,9 +22,9 @@ import { fromError } from "zod-validation-error"; import { eq, and } from "drizzle-orm"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { subdomainSchema } from "@server/lib/schemas"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { createCertificate } from "#private/routers/certificates/createCertificate"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; const paramsSchema = z diff --git a/server/private/routers/org/index.ts b/server/private/routers/org/index.ts new file mode 100644 index 00000000..189c5323 --- /dev/null +++ b/server/private/routers/org/index.ts @@ -0,0 +1,14 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./sendUsageNotifications"; \ No newline at end of file diff --git a/server/routers/org/privateSendUsageNotifications.ts b/server/private/routers/org/sendUsageNotifications.ts similarity index 98% rename from server/routers/org/privateSendUsageNotifications.ts rename to server/private/routers/org/sendUsageNotifications.ts index 8b2a773d..3ef27f91 100644 --- a/server/routers/org/privateSendUsageNotifications.ts +++ b/server/private/routers/org/sendUsageNotifications.ts @@ -22,8 +22,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { sendEmail } from "@server/emails"; -import NotifyUsageLimitApproaching from "@server/emails/templates/PrivateNotifyUsageLimitApproaching"; -import NotifyUsageLimitReached from "@server/emails/templates/PrivateNotifyUsageLimitReached"; +import NotifyUsageLimitApproaching from "@server/emails/templates/NotifyUsageLimitApproaching"; +import NotifyUsageLimitReached from "@server/emails/templates/NotifyUsageLimitReached"; import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; diff --git a/server/routers/private/orgIdp/createOrgOidcIdp.ts b/server/private/routers/orgIdp/createOrgOidcIdp.ts similarity index 97% rename from server/routers/private/orgIdp/createOrgOidcIdp.ts rename to server/private/routers/orgIdp/createOrgOidcIdp.ts index 16697f98..5f732864 100644 --- a/server/routers/private/orgIdp/createOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/createOrgOidcIdp.ts @@ -25,8 +25,8 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict(); diff --git a/server/private/routers/orgIdp/deleteOrgIdp.ts b/server/private/routers/orgIdp/deleteOrgIdp.ts new file mode 100644 index 00000000..711d1ce3 --- /dev/null +++ b/server/private/routers/orgIdp/deleteOrgIdp.ts @@ -0,0 +1,108 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromError } from "zod-validation-error"; +import { idp, idpOidcConfig, idpOrg } from "@server/db"; +import { eq } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; + +const paramsSchema = z + .object({ + orgId: z.string().optional(), // Optional; used with org idp in saas + idpId: z.coerce.number() + }) + .strict(); + +registry.registerPath({ + method: "delete", + path: "/idp/{idpId}", + description: "Delete IDP.", + tags: [OpenAPITags.Idp], + request: { + params: paramsSchema + }, + responses: {} +}); + +export async function deleteOrgIdp( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { idpId } = parsedParams.data; + + // Check if IDP exists + const [existingIdp] = await db + .select() + .from(idp) + .where(eq(idp.idpId, idpId)); + + if (!existingIdp) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "IdP not found" + ) + ); + } + + // Delete the IDP and its related records in a transaction + await db.transaction(async (trx) => { + // Delete OIDC config if it exists + await trx + .delete(idpOidcConfig) + .where(eq(idpOidcConfig.idpId, idpId)); + + // Delete IDP-org mappings + await trx + .delete(idpOrg) + .where(eq(idpOrg.idpId, idpId)); + + // Delete the IDP itself + await trx + .delete(idp) + .where(eq(idp.idpId, idpId)); + }); + + return response(res, { + data: null, + success: true, + error: false, + message: "IdP deleted successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/getOrgIdp.ts b/server/private/routers/orgIdp/getOrgIdp.ts similarity index 100% rename from server/routers/private/orgIdp/getOrgIdp.ts rename to server/private/routers/orgIdp/getOrgIdp.ts diff --git a/server/routers/private/orgIdp/index.ts b/server/private/routers/orgIdp/index.ts similarity index 87% rename from server/routers/private/orgIdp/index.ts rename to server/private/routers/orgIdp/index.ts index 99c30654..562582c6 100644 --- a/server/routers/private/orgIdp/index.ts +++ b/server/private/routers/orgIdp/index.ts @@ -14,4 +14,5 @@ export * from "./createOrgOidcIdp"; export * from "./getOrgIdp"; export * from "./listOrgIdps"; -export * from "./updateOrgOidcIdp"; \ No newline at end of file +export * from "./updateOrgOidcIdp"; +export * from "./deleteOrgIdp"; \ No newline at end of file diff --git a/server/routers/private/orgIdp/listOrgIdps.ts b/server/private/routers/orgIdp/listOrgIdps.ts similarity index 100% rename from server/routers/private/orgIdp/listOrgIdps.ts rename to server/private/routers/orgIdp/listOrgIdps.ts diff --git a/server/routers/private/orgIdp/updateOrgOidcIdp.ts b/server/private/routers/orgIdp/updateOrgOidcIdp.ts similarity index 97% rename from server/routers/private/orgIdp/updateOrgOidcIdp.ts rename to server/private/routers/orgIdp/updateOrgOidcIdp.ts index a3be85c3..c6e54240 100644 --- a/server/routers/private/orgIdp/updateOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/updateOrgOidcIdp.ts @@ -24,10 +24,9 @@ import { idp, idpOidcConfig } from "@server/db"; import { eq, and } from "drizzle-orm"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; -import license from "@server/license/license"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/private/remoteExitNode/createRemoteExitNode.ts b/server/private/routers/remoteExitNode/createRemoteExitNode.ts similarity index 97% rename from server/routers/private/remoteExitNode/createRemoteExitNode.ts rename to server/private/routers/remoteExitNode/createRemoteExitNode.ts index ac4fd231..44fecb86 100644 --- a/server/routers/private/remoteExitNode/createRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/createRemoteExitNode.ts @@ -21,14 +21,14 @@ import response from "@server/lib/response"; import { SqliteError } from "better-sqlite3"; import moment from "moment"; import { generateSessionToken } from "@server/auth/sessions/app"; -import { createRemoteExitNodeSession } from "@server/auth/sessions/privateRemoteExitNode"; +import { createRemoteExitNodeSession } from "#private/auth/sessions/remoteExitNode"; import { fromError } from "zod-validation-error"; import { hashPassword, verifyPassword } from "@server/auth/password"; import logger from "@server/logger"; import { and, eq } from "drizzle-orm"; import { getNextAvailableSubnet } from "@server/lib/exitNodes"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; export const paramsSchema = z.object({ orgId: z.string() diff --git a/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts similarity index 96% rename from server/routers/private/remoteExitNode/deleteRemoteExitNode.ts rename to server/private/routers/remoteExitNode/deleteRemoteExitNode.ts index 84ef0fab..f7b9d56c 100644 --- a/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts @@ -21,8 +21,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const paramsSchema = z .object({ diff --git a/server/routers/private/remoteExitNode/getRemoteExitNode.ts b/server/private/routers/remoteExitNode/getRemoteExitNode.ts similarity index 100% rename from server/routers/private/remoteExitNode/getRemoteExitNode.ts rename to server/private/routers/remoteExitNode/getRemoteExitNode.ts diff --git a/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts similarity index 98% rename from server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts rename to server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts index 3905f1f7..48b0110d 100644 --- a/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts @@ -24,7 +24,7 @@ import { fromError } from "zod-validation-error"; import { createRemoteExitNodeSession, validateRemoteExitNodeSessionToken -} from "@server/auth/sessions/privateRemoteExitNode"; +} from "#private/auth/sessions/remoteExitNode"; import { verifyPassword } from "@server/auth/password"; import logger from "@server/logger"; import config from "@server/lib/config"; diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts similarity index 100% rename from server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts rename to server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts similarity index 100% rename from server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts rename to server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts diff --git a/server/routers/private/remoteExitNode/index.ts b/server/private/routers/remoteExitNode/index.ts similarity index 100% rename from server/routers/private/remoteExitNode/index.ts rename to server/private/routers/remoteExitNode/index.ts diff --git a/server/routers/private/remoteExitNode/listRemoteExitNodes.ts b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts similarity index 100% rename from server/routers/private/remoteExitNode/listRemoteExitNodes.ts rename to server/private/routers/remoteExitNode/listRemoteExitNodes.ts diff --git a/server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts similarity index 100% rename from server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts rename to server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts diff --git a/server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts similarity index 100% rename from server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts rename to server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts diff --git a/src/lib/types/privateThemeTypes.tsx b/server/private/routers/ws/index.ts similarity index 94% rename from src/lib/types/privateThemeTypes.tsx rename to server/private/routers/ws/index.ts index de0b2d2b..4d803a3a 100644 --- a/src/lib/types/privateThemeTypes.tsx +++ b/server/private/routers/ws/index.ts @@ -11,3 +11,4 @@ * This file is not licensed under the AGPLv3. */ +export * from "./ws"; \ No newline at end of file diff --git a/server/private/routers/ws/messageHandlers.ts b/server/private/routers/ws/messageHandlers.ts new file mode 100644 index 00000000..71c2b253 --- /dev/null +++ b/server/private/routers/ws/messageHandlers.ts @@ -0,0 +1,26 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + handleRemoteExitNodeRegisterMessage, + handleRemoteExitNodePingMessage, + startRemoteExitNodeOfflineChecker +} from "#private/routers/remoteExitNode"; +import { MessageHandler } from "@server/routers/ws"; + +export const messageHandlers: Record = { + "remoteExitNode/register": handleRemoteExitNodeRegisterMessage, + "remoteExitNode/ping": handleRemoteExitNodePingMessage +}; + +startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes \ No newline at end of file diff --git a/server/routers/ws/privateWs.ts b/server/private/routers/ws/ws.ts similarity index 94% rename from server/routers/ws/privateWs.ts rename to server/private/routers/ws/ws.ts index d94ccf5e..0122126f 100644 --- a/server/routers/ws/privateWs.ts +++ b/server/private/routers/ws/ws.ts @@ -14,7 +14,6 @@ import { Router, Request, Response } from "express"; import { Server as HttpServer } from "http"; import { WebSocket, WebSocketServer } from "ws"; -import { IncomingMessage } from "http"; import { Socket } from "net"; import { Newt, @@ -31,73 +30,20 @@ import { eq } from "drizzle-orm"; import { db } from "@server/db"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; import { validateOlmSessionToken } from "@server/auth/sessions/olm"; -import { messageHandlers } from "./messageHandlers"; import logger from "@server/logger"; -import redisManager from "@server/db/private/redis"; +import redisManager from "#private/lib/redis"; import { v4 as uuidv4 } from "uuid"; -import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; -import { rateLimitService } from "@server/db/private/rateLimit"; +import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remoteExitNode"; +import { rateLimitService } from "#private/lib/rateLimit"; +import { messageHandlers } from "@server/routers/ws/messageHandlers"; +import { messageHandlers as privateMessageHandlers } from "#private/routers/ws/messageHandlers"; +import { AuthenticatedWebSocket, ClientType, WSMessage, TokenPayload, WebSocketRequest, RedisMessage } from "@server/routers/ws"; + +// Merge public and private message handlers +Object.assign(messageHandlers, privateMessageHandlers); const MAX_PENDING_MESSAGES = 50; // Maximum messages to queue during connection setup -// Custom interfaces -interface WebSocketRequest extends IncomingMessage { - token?: string; -} - -type ClientType = "newt" | "olm" | "remoteExitNode"; - -interface AuthenticatedWebSocket extends WebSocket { - client?: Newt | Olm | RemoteExitNode; - clientType?: ClientType; - connectionId?: string; - isFullyConnected?: boolean; - pendingMessages?: Buffer[]; -} - -interface TokenPayload { - client: Newt | Olm | RemoteExitNode; - session: NewtSession | OlmSession | RemoteExitNodeSession; - clientType: ClientType; -} - -interface WSMessage { - type: string; - data: any; -} - -interface HandlerResponse { - message: WSMessage; - broadcast?: boolean; - excludeSender?: boolean; - targetClientId?: string; -} - -interface HandlerContext { - message: WSMessage; - senderWs: WebSocket; - client: Newt | Olm | RemoteExitNode | undefined; - clientType: ClientType; - sendToClient: (clientId: string, message: WSMessage) => Promise; - broadcastToAllExcept: ( - message: WSMessage, - excludeClientId?: string - ) => Promise; - connectedClients: Map; -} - -interface RedisMessage { - type: "direct" | "broadcast"; - targetClientId?: string; - excludeClientId?: string; - message: WSMessage; - fromNodeId: string; -} - -export type MessageHandler = ( - context: HandlerContext -) => Promise; - // Helper function to process a single message const processMessage = async ( ws: AuthenticatedWebSocket, @@ -875,10 +821,6 @@ const cleanup = async (): Promise => { } }; -// Handle process termination -process.on("SIGTERM", cleanup); -process.on("SIGINT", cleanup); - export { router, handleWSUpgrade, diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 9db5931a..754478fc 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -10,10 +10,7 @@ export * from "./resetPassword"; export * from "./requestPasswordReset"; export * from "./setServerAdmin"; export * from "./initialSetupComplete"; -export * from "./privateQuickStart"; export * from "./validateSetupToken"; export * from "./changePassword"; export * from "./checkResourceSession"; -export * from "./securityKey"; -export * from "./privateGetSessionTransferToken"; -export * from "./privateTransferSession"; +export * from "./securityKey"; \ No newline at end of file diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index e6ae4fe4..7c122a44 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -110,10 +110,12 @@ export async function requestTotpSecret( ); } + const appName = process.env.BRANDING_APP_NAME || "Pangolin"; // From the private config loading into env vars to seperate away the private config + const hex = crypto.getRandomValues(new Uint8Array(20)); const secret = encodeHex(hex); const uri = createTOTPKeyURI( - config.getRawPrivateConfig().branding?.app_name || "Pangolin", + appName, user.email!, hex ); diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index 0d4f6865..1f361b79 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -21,12 +21,12 @@ import { hashPassword } from "@server/auth/password"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { passwordSchema } from "@server/auth/passwordSchema"; import { UserType } from "@server/types/UserTypes"; -import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; +import { createUserAccountOrg } from "@server/lib/createUserAccountOrg"; import { build } from "@server/build"; import resend, { AudienceIds, moveEmailToAudience -} from "@server/lib/private/resend"; +} from "#dynamic/lib/resend"; export const signupBodySchema = z.object({ email: z.string().toLowerCase().email(), diff --git a/server/routers/auth/verifyEmail.ts b/server/routers/auth/verifyEmail.ts index 010ddf28..47a81c0a 100644 --- a/server/routers/auth/verifyEmail.ts +++ b/server/routers/auth/verifyEmail.ts @@ -10,7 +10,7 @@ import { eq } from "drizzle-orm"; import { isWithinExpirationDate } from "oslo"; import config from "@server/lib/config"; import logger from "@server/logger"; -import { freeLimitSet, limitsService } from "@server/lib/private/billing"; +import { freeLimitSet, limitsService } from "@server/lib/billing"; import { build } from "@server/build"; export const verifyEmailBody = z diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index c380e679..4a000144 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -34,8 +34,8 @@ import NodeCache from "node-cache"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import { getCountryCodeForIp, remoteGetCountryCodeForIp } from "@server/lib/geoip"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { verifyPassword } from "@server/auth/password"; // We'll see if this speeds anything up diff --git a/server/routers/billing/webhooks.ts b/server/routers/billing/webhooks.ts new file mode 100644 index 00000000..0ca38a8a --- /dev/null +++ b/server/routers/billing/webhooks.ts @@ -0,0 +1,14 @@ +import createHttpError from "http-errors"; +import { Request, Response, NextFunction } from "express"; +import HttpCode from "@server/types/HttpCode"; + +export async function billingWebhookHandler( + req: Request, + res: Response, + next: NextFunction +): Promise { + // return not found + return next( + createHttpError(HttpCode.NOT_FOUND, "This endpoint is not in use") + ); +} \ No newline at end of file diff --git a/server/routers/certificates/createCertificate.ts b/server/routers/certificates/createCertificate.ts new file mode 100644 index 00000000..e160e644 --- /dev/null +++ b/server/routers/certificates/createCertificate.ts @@ -0,0 +1,5 @@ +import { db, Transaction } from "@server/db"; + +export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { + return; +} \ No newline at end of file diff --git a/server/routers/client/createClient.ts b/server/routers/client/createClient.ts index e7762223..cb2bbd6e 100644 --- a/server/routers/client/createClient.ts +++ b/server/routers/client/createClient.ts @@ -24,7 +24,7 @@ import { hashPassword } from "@server/auth/password"; import { isValidCIDR, isValidIP } from "@server/lib/validators"; import { isIpInCidr } from "@server/lib/ip"; import { OpenAPITags, registry } from "@server/openApi"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; const createClientParamsSchema = z .object({ diff --git a/server/routers/client/targets.ts b/server/routers/client/targets.ts index e34a23e9..e5c46d70 100644 --- a/server/routers/client/targets.ts +++ b/server/routers/client/targets.ts @@ -1,4 +1,4 @@ -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; export async function addTargets( newtId: string, diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index 80050f6c..884a9864 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -17,7 +17,7 @@ import { addPeer as olmAddPeer, deletePeer as olmDeletePeer } from "../olm/peers"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; const updateClientParamsSchema = z .object({ diff --git a/server/routers/domain/createOrgDomain.ts b/server/routers/domain/createOrgDomain.ts index 3744f044..4e2acdbf 100644 --- a/server/routers/domain/createOrgDomain.ts +++ b/server/routers/domain/createOrgDomain.ts @@ -9,8 +9,8 @@ import { fromError } from "zod-validation-error"; import { subdomainSchema } from "@server/lib/schemas"; import { generateId } from "@server/auth/sessions/app"; import { eq, and } from "drizzle-orm"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators"; import { build } from "@server/build"; import config from "@server/lib/config"; diff --git a/server/routers/domain/deleteOrgDomain.ts b/server/routers/domain/deleteOrgDomain.ts index 8932733c..8836584b 100644 --- a/server/routers/domain/deleteOrgDomain.ts +++ b/server/routers/domain/deleteOrgDomain.ts @@ -7,8 +7,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { and, eq } from "drizzle-orm"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const paramsSchema = z .object({ diff --git a/server/routers/domain/index.ts b/server/routers/domain/index.ts index e833e532..c0cafafe 100644 --- a/server/routers/domain/index.ts +++ b/server/routers/domain/index.ts @@ -1,6 +1,4 @@ export * from "./listDomains"; export * from "./createOrgDomain"; export * from "./deleteOrgDomain"; -export * from "./privateListDomainNamespaces"; -export * from "./privateCheckDomainNamespaceAvailability"; export * from "./restartOrgDomain"; \ No newline at end of file diff --git a/server/routers/external.ts b/server/routers/external.ts index 3a96bb03..d90b7478 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -38,25 +38,13 @@ import { verifyUserIsOrgOwner, verifySiteResourceAccess } from "@server/middlewares"; -import { - verifyCertificateAccess, - verifyRemoteExitNodeAccess, - verifyIdpAccess, - verifyLoginPageAccess -} from "@server/middlewares/private"; -import { createStore } from "@server/lib/private/rateLimitStore"; import { ActionsEnum } from "@server/auth/actions"; import { createNewt, getNewtToken } from "./newt"; import { getOlmToken } from "./olm"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; -import * as certificates from "./private/certificates"; -import * as billing from "@server/routers/private/billing"; -import { quickStart } from "./auth/privateQuickStart"; import { build } from "@server/build"; -import * as remoteExitNode from "@server/routers/private/remoteExitNode"; -import * as loginPage from "@server/routers/private/loginPage"; -import * as orgIdp from "@server/routers/private/orgIdp"; +import { createStore } from "#dynamic/lib/rateLimitStore"; // Root routes export const unauthenticated = Router(); @@ -65,45 +53,6 @@ unauthenticated.get("/", (_, res) => { res.status(HttpCode.OK).json({ message: "Healthy" }); }); -if (build === "saas") { - unauthenticated.post( - "/quick-start", - rateLimit({ - windowMs: 15 * 60 * 1000, - max: 100, - keyGenerator: (req) => req.path, - handler: (req, res, next) => { - const message = `We're too busy right now. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - quickStart - ); -} - -if (build !== "oss") { - unauthenticated.post( - "/remote-exit-node/quick-start", - rateLimit({ - windowMs: 60 * 60 * 1000, - max: 5, - keyGenerator: (req) => - `${req.path}:${ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only create 5 remote exit nodes every hour. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - remoteExitNode.quickStartRemoteExitNode - ); -} - // Authenticated Root routes export const authenticated = Router(); authenticated.use(verifySessionUserMiddleware); @@ -727,45 +676,7 @@ authenticated.post( idp.updateOidcIdp ); -if (build !== "oss") { - authenticated.put( - "/org/:orgId/idp/oidc", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createIdp), - orgIdp.createOrgOidcIdp - ); - authenticated.post( - "/org/:orgId/idp/:idpId/oidc", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.updateIdp), - orgIdp.updateOrgOidcIdp - ); - - authenticated.delete( - "/org/:orgId/idp/:idpId", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.deleteIdp), - idp.deleteIdp - ); - - authenticated.get( - "/org/:orgId/idp/:idpId", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.getIdp), - orgIdp.getOrgIdp - ); - - authenticated.get( - "/org/:orgId/idp", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.listIdps), - orgIdp.listOrgIdps - ); -} authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp); @@ -795,9 +706,7 @@ authenticated.get( idp.listIdpOrgPolicies ); -if (build !== "oss") { - authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids -} + authenticated.get("/idp", idp.listIdps); // anyone can see this; it's just a list of idp names and ids authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); @@ -930,126 +839,6 @@ authenticated.delete( domain.deleteAccountDomain ); -if (build !== "oss") { - authenticated.get( - "/org/:orgId/certificate/:domainId/:domain", - verifyOrgAccess, - verifyCertificateAccess, - verifyUserHasAction(ActionsEnum.getCertificate), - certificates.getCertificate - ); - - authenticated.post( - "/org/:orgId/certificate/:certId/restart", - verifyOrgAccess, - verifyCertificateAccess, - verifyUserHasAction(ActionsEnum.restartCertificate), - certificates.restartCertificate - ); - - authenticated.post( - "/org/:orgId/billing/create-checkout-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createCheckoutSession - ); - - authenticated.post( - "/org/:orgId/billing/create-portal-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createPortalSession - ); - - authenticated.get( - "/org/:orgId/billing/subscription", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgSubscription - ); - - authenticated.get( - "/org/:orgId/billing/usage", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgUsage - ); - - authenticated.get("/domain/namespaces", domain.listDomainNamespaces); - - authenticated.get( - "/domain/check-namespace-availability", - domain.checkDomainNamespaceAvailability - ); - - authenticated.put( - "/org/:orgId/remote-exit-node", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createRemoteExitNode), - remoteExitNode.createRemoteExitNode - ); - - authenticated.get( - "/org/:orgId/remote-exit-nodes", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.listRemoteExitNode), - remoteExitNode.listRemoteExitNodes - ); - - authenticated.get( - "/org/:orgId/remote-exit-node/:remoteExitNodeId", - verifyOrgAccess, - verifyRemoteExitNodeAccess, - verifyUserHasAction(ActionsEnum.getRemoteExitNode), - remoteExitNode.getRemoteExitNode - ); - - authenticated.get( - "/org/:orgId/pick-remote-exit-node-defaults", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createRemoteExitNode), - remoteExitNode.pickRemoteExitNodeDefaults - ); - - authenticated.delete( - "/org/:orgId/remote-exit-node/:remoteExitNodeId", - verifyOrgAccess, - verifyRemoteExitNodeAccess, - verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), - remoteExitNode.deleteRemoteExitNode - ); - - authenticated.put( - "/org/:orgId/login-page", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createLoginPage), - loginPage.createLoginPage - ); - - authenticated.post( - "/org/:orgId/login-page/:loginPageId", - verifyOrgAccess, - verifyLoginPageAccess, - verifyUserHasAction(ActionsEnum.updateLoginPage), - loginPage.updateLoginPage - ); - - authenticated.delete( - "/org/:orgId/login-page/:loginPageId", - verifyOrgAccess, - verifyLoginPageAccess, - verifyUserHasAction(ActionsEnum.deleteLoginPage), - loginPage.deleteLoginPage - ); - - authenticated.get( - "/org/:orgId/login-page", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.getLoginPage), - loginPage.getLoginPage - ); -} - // Auth routes export const authRouter = Router(); unauthenticated.use("/auth", authRouter); @@ -1129,26 +918,6 @@ authRouter.post( getOlmToken ); -if (build !== "oss") { - authRouter.post( - "/remoteExitNode/get-token", - rateLimit({ - windowMs: 15 * 60 * 1000, - max: 900, - keyGenerator: (req) => - `remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - remoteExitNode.getRemoteExitNodeToken - ); -} - authRouter.post( "/2fa/enable", rateLimit({ @@ -1316,26 +1085,6 @@ authRouter.post( resource.authWithWhitelist ); -if (build !== "oss") { - authRouter.post( - "/transfer-session-token", - rateLimit({ - windowMs: 1 * 60 * 1000, - max: 60, - keyGenerator: (req) => - `transferSessionToken:${ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - auth.transferSession - ); -} - authRouter.post( "/resource/:resourceId/access-token", resource.authWithAccessToken diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index afae4009..1604dc30 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -1,19 +1,16 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { sites, resources, targets, exitNodes, ExitNode } from "@server/db"; +import { sites, exitNodes, ExitNode } from "@server/db"; import { db } from "@server/db"; import { eq, isNotNull, and } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; -import { getUniqueExitNodeEndpointName } from "../../db/names"; -import { findNextAvailableCidr } from "@server/lib/ip"; import { fromError } from "zod-validation-error"; import { getAllowedIps } from "../target/helpers"; import { proxyToRemote } from "@server/lib/remoteProxy"; -import { getNextAvailableSubnet } from "@server/lib/exitNodes"; -import { createExitNode } from "./privateCreateExitNode"; +import { createExitNode } from "#dynamic/routers/gerbil/createExitNode"; // Define Zod schema for request validation const getConfigSchema = z.object({ diff --git a/server/routers/gerbil/getResolvedHostname.ts b/server/routers/gerbil/getResolvedHostname.ts index 17067c55..f06cd1b8 100644 --- a/server/routers/gerbil/getResolvedHostname.ts +++ b/server/routers/gerbil/getResolvedHostname.ts @@ -4,9 +4,9 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { resolveExitNodes } from "@server/lib/exitNodes"; -import config from "@server/lib/config"; +import { resolveExitNodes } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; +import config from "@server/lib/config"; // Define Zod schema for request validation const getResolvedHostnameSchema = z.object({ @@ -36,7 +36,15 @@ export async function getResolvedHostname( const { hostname, publicKey } = parsedParams.data; - const baseDomain = config.getRawPrivateConfig().app.base_domain; + const dashboardUrl = config.getRawConfig().app.dashboard_url; + + // extract the domain removing the http and stuff + const baseDomain = dashboardUrl + ? dashboardUrl + .replace("http://", "") + .replace("https://", "") + .split("/")[0] + : null; // if the hostname ends with the base domain then send back a empty array if (baseDomain && hostname.endsWith(baseDomain)) { @@ -50,6 +58,13 @@ export async function getResolvedHostname( publicKey ); + if (resourceExitNodes.length === 0) { + // no exit nodes found, return empty array to force local routing + return res.status(HttpCode.OK).send({ + endpoints: [] // this should force to route locally + }); + } + endpoints = resourceExitNodes.map((node) => node.endpoint); } diff --git a/server/routers/gerbil/peers.ts b/server/routers/gerbil/peers.ts index 1cdc9184..44af7fbd 100644 --- a/server/routers/gerbil/peers.ts +++ b/server/routers/gerbil/peers.ts @@ -2,7 +2,7 @@ import logger from "@server/logger"; import { db } from "@server/db"; import { exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; export async function addPeer( exitNodeId: number, diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index d10141b9..3661dedd 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -6,9 +6,9 @@ import logger from "@server/logger"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing/features"; -import { checkExitNodeOrg } from "@server/lib/exitNodes"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing/features"; +import { checkExitNodeOrg } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; // Track sites that are already offline to avoid unnecessary queries diff --git a/server/routers/gerbil/updateHolePunch.ts b/server/routers/gerbil/updateHolePunch.ts index 65217178..34bc2c6b 100644 --- a/server/routers/gerbil/updateHolePunch.ts +++ b/server/routers/gerbil/updateHolePunch.ts @@ -18,8 +18,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; import { validateOlmSessionToken } from "@server/auth/sessions/olm"; -import axios from "axios"; -import { checkExitNodeOrg } from "@server/lib/exitNodes"; +import { checkExitNodeOrg } from "#dynamic/lib/exitNodes"; // Define Zod schema for request validation const updateHolePunchSchema = z.object({ diff --git a/server/routers/hybrid.ts b/server/routers/hybrid.ts new file mode 100644 index 00000000..235961f1 --- /dev/null +++ b/server/routers/hybrid.ts @@ -0,0 +1,4 @@ +import { Router } from "express"; + +// Root routes +export const hybridRouter = Router(); \ No newline at end of file diff --git a/server/routers/idp/generateOidcUrl.ts b/server/routers/idp/generateOidcUrl.ts index 90144816..3c81ce0b 100644 --- a/server/routers/idp/generateOidcUrl.ts +++ b/server/routers/idp/generateOidcUrl.ts @@ -14,8 +14,8 @@ import jsonwebtoken from "jsonwebtoken"; import config from "@server/lib/config"; import { decrypt } from "@server/lib/crypto"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index fec21e41..0ecf08b0 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -30,8 +30,8 @@ import { } from "@server/auth/sessions/app"; import { decrypt } from "@server/lib/crypto"; import { UserType } from "@server/types/UserTypes"; -import { FeatureId } from "@server/lib/private/billing"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; const ensureTrailingSlash = (url: string): string => { return url; diff --git a/server/routers/integration.ts b/server/routers/integration.ts index 879075c7..d0c7c5d5 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -555,13 +555,6 @@ authenticated.post( idp.updateOidcIdp ); -authenticated.delete( - "/idp/:idpId", - verifyApiKeyIsRoot, - verifyApiKeyHasAction(ActionsEnum.deleteIdp), - idp.deleteIdp -); - authenticated.get( "/idp", verifyApiKeyIsRoot, @@ -604,15 +597,6 @@ authenticated.get( idp.listIdpOrgPolicies ); -if (build == "saas") { - authenticated.post( - `/org/:orgId/send-usage-notification`, - verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine - verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), - org.sendUsageNotification - ); -} - authenticated.get( "/org/:orgId/pick-client-defaults", verifyClientsEnabled, diff --git a/server/routers/internal.ts b/server/routers/internal.ts index e4525118..10966bb5 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -7,7 +7,6 @@ import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; import * as license from "@server/routers/license"; import * as idp from "@server/routers/idp"; -import * as loginPage from "@server/routers/private/loginPage"; import { proxyToRemote } from "@server/lib/remoteProxy"; import config from "@server/lib/config"; import HttpCode from "@server/types/HttpCode"; @@ -15,12 +14,9 @@ import { verifyResourceAccess, verifySessionUserMiddleware } from "@server/middlewares"; -import { build } from "@server/build"; -import * as billing from "@server/routers/private/billing"; -import * as orgIdp from "@server/routers/private/orgIdp"; // Root routes -const internalRouter = Router(); +export const internalRouter = Router(); internalRouter.get("/", (_, res) => { res.status(HttpCode.OK).json({ message: "Healthy" }); @@ -51,12 +47,6 @@ internalRouter.get("/idp", idp.listIdps); internalRouter.get("/idp/:idpId", idp.getIdp); -if (build !== "oss") { - internalRouter.get("/org/:orgId/idp", orgIdp.listOrgIdps); - - internalRouter.get("/org/:orgId/billing/tier", billing.getOrgTier); -} - // Gerbil routes const gerbilRouter = Router(); internalRouter.use("/gerbil", gerbilRouter); @@ -106,16 +96,4 @@ if (config.isManagedMode()) { ); } else { badgerRouter.post("/exchange-session", badger.exchangeSession); -} - -if (build !== "oss") { - internalRouter.get("/login-page", loginPage.loadLoginPage); - - internalRouter.post( - "/get-session-transfer-token", - verifySessionUserMiddleware, - auth.getSessionTransferToken - ); -} - -export default internalRouter; +} \ No newline at end of file diff --git a/server/routers/newt/dockerSocket.ts b/server/routers/newt/dockerSocket.ts index 0c59d354..3847d9a4 100644 --- a/server/routers/newt/dockerSocket.ts +++ b/server/routers/newt/dockerSocket.ts @@ -1,5 +1,5 @@ import NodeCache from "node-cache"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; export const dockerSocketCache = new NodeCache({ stdTTL: 3600 // seconds diff --git a/server/routers/newt/handleApplyBlueprintMessage.ts b/server/routers/newt/handleApplyBlueprintMessage.ts index 68158799..62802fff 100644 --- a/server/routers/newt/handleApplyBlueprintMessage.ts +++ b/server/routers/newt/handleApplyBlueprintMessage.ts @@ -1,5 +1,5 @@ import { db, newts } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { eq, and, sql, inArray } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/newt/handleGetConfigMessage.ts b/server/routers/newt/handleGetConfigMessage.ts index 2b65fd06..3eba94b9 100644 --- a/server/routers/newt/handleGetConfigMessage.ts +++ b/server/routers/newt/handleGetConfigMessage.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { @@ -14,7 +14,7 @@ import { import { clients, clientSites, Newt, sites } from "@server/db"; import { eq, and, inArray } from "drizzle-orm"; import { updatePeer } from "../olm/peers"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; const inputSchema = z.object({ publicKey: z.string(), diff --git a/server/routers/newt/handleNewtPingRequestMessage.ts b/server/routers/newt/handleNewtPingRequestMessage.ts index aeb7a155..fea157fd 100644 --- a/server/routers/newt/handleNewtPingRequestMessage.ts +++ b/server/routers/newt/handleNewtPingRequestMessage.ts @@ -1,10 +1,9 @@ import { db, sites } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt } from "@server/db"; import logger from "@server/logger"; -import config from "@server/lib/config"; import { ne, eq, or, and, count } from "drizzle-orm"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export const handleNewtPingRequestMessage: MessageHandler = async (context) => { const { message, client, sendToClient } = context; diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 021527ad..372f3677 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -1,5 +1,5 @@ import { db, exitNodeOrgs, newts } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { targetHealthCheck } from "@server/db"; import { eq, and, sql, inArray } from "drizzle-orm"; @@ -10,12 +10,12 @@ import { findNextAvailableCidr, getNextAvailableClientSubnet } from "@server/lib/ip"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { selectBestExitNode, verifyExitNodeOrgAccess -} from "@server/lib/exitNodes"; +} from "#dynamic/lib/exitNodes"; import { fetchContainers } from "./dockerSocket"; export type ExitNodePingResult = { diff --git a/server/routers/newt/handleReceiveBandwidthMessage.ts b/server/routers/newt/handleReceiveBandwidthMessage.ts index 89b24f78..f5170feb 100644 --- a/server/routers/newt/handleReceiveBandwidthMessage.ts +++ b/server/routers/newt/handleReceiveBandwidthMessage.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, Newt } from "@server/db"; import { eq } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/newt/handleSocketMessages.ts b/server/routers/newt/handleSocketMessages.ts index aceca37d..0491393f 100644 --- a/server/routers/newt/handleSocketMessages.ts +++ b/server/routers/newt/handleSocketMessages.ts @@ -1,4 +1,4 @@ -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import logger from "@server/logger"; import { dockerSocketCache } from "./dockerSocket"; import { Newt } from "@server/db"; diff --git a/server/routers/newt/peers.ts b/server/routers/newt/peers.ts index ff57e6fd..03dc3460 100644 --- a/server/routers/newt/peers.ts +++ b/server/routers/newt/peers.ts @@ -1,7 +1,7 @@ import { db } from "@server/db"; import { newts, sites } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; export async function addPeer( diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index a886b00b..97e4030d 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -1,5 +1,5 @@ import { Target, TargetHealthCheck, db, targetHealthCheck } from "@server/db"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; import { eq, inArray } from "drizzle-orm"; diff --git a/server/routers/olm/handleOlmPingMessage.ts b/server/routers/olm/handleOlmPingMessage.ts index 6c4b5600..6f00640d 100644 --- a/server/routers/olm/handleOlmPingMessage.ts +++ b/server/routers/olm/handleOlmPingMessage.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, Olm } from "@server/db"; import { eq, lt, isNull, and, or } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/olm/handleOlmRegisterMessage.ts b/server/routers/olm/handleOlmRegisterMessage.ts index 33d7f9cb..66128f0e 100644 --- a/server/routers/olm/handleOlmRegisterMessage.ts +++ b/server/routers/olm/handleOlmRegisterMessage.ts @@ -1,10 +1,10 @@ import { db, ExitNode } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, clientSites, exitNodes, Olm, olms, sites } from "@server/db"; import { and, eq, inArray } from "drizzle-orm"; import { addPeer, deletePeer } from "../newt/peers"; import logger from "@server/logger"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.info("Handling register olm message!"); diff --git a/server/routers/olm/handleOlmRelayMessage.ts b/server/routers/olm/handleOlmRelayMessage.ts index cefc5b91..9b31754c 100644 --- a/server/routers/olm/handleOlmRelayMessage.ts +++ b/server/routers/olm/handleOlmRelayMessage.ts @@ -1,5 +1,5 @@ import { db, exitNodes, sites } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, clientSites, Olm } from "@server/db"; import { and, eq } from "drizzle-orm"; import { updatePeer } from "../newt/peers"; diff --git a/server/routers/olm/peers.ts b/server/routers/olm/peers.ts index a8d6be9c..396866a1 100644 --- a/server/routers/olm/peers.ts +++ b/server/routers/olm/peers.ts @@ -1,7 +1,7 @@ import { db } from "@server/db"; import { clients, olms, newts, sites } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; export async function addPeer( diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index a12520a4..d8bcb9da 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -3,8 +3,6 @@ import { z } from "zod"; import { db } from "@server/db"; import { eq } from "drizzle-orm"; import { - apiKeyOrg, - apiKeys, domains, Org, orgDomains, @@ -24,9 +22,9 @@ import { fromError } from "zod-validation-error"; import { defaultRoleAllowedActions } from "../role"; import { OpenAPITags, registry } from "@server/openApi"; import { isValidCIDR } from "@server/lib/validators"; -import { createCustomer } from "@server/routers/private/billing/createCustomer"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { createCustomer } from "#dynamic/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; const createOrgSchema = z diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index 63e9abb0..8a424e5b 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { deletePeer } from "../gerbil/peers"; import { OpenAPITags, registry } from "@server/openApi"; diff --git a/server/routers/org/index.ts b/server/routers/org/index.ts index 013f6c6d..7887fcac 100644 --- a/server/routers/org/index.ts +++ b/server/routers/org/index.ts @@ -7,5 +7,4 @@ export * from "./checkId"; export * from "./getOrgOverview"; export * from "./listOrgs"; export * from "./pickOrgDefaults"; -export * from "./privateSendUsageNotifications"; export * from "./applyBlueprint"; diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 53af0b72..f4fe352e 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -21,7 +21,7 @@ import { subdomainSchema } from "@server/lib/schemas"; import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; import { build } from "@server/build"; -import { createCertificate } from "../private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { getUniqueResourceName } from "@server/db/names"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 83fcf6f1..a9c3b5de 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -20,7 +20,7 @@ import { tlsNameSchema } from "@server/lib/schemas"; import { subdomainSchema } from "@server/lib/schemas"; import { registry } from "@server/openApi"; import { OpenAPITags } from "@server/openApi"; -import { createCertificate } from "../private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { validateHeaders } from "@server/lib/validators"; import { build } from "@server/build"; diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 5ffa6954..0ffc5956 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -16,8 +16,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import { hashPassword } from "@server/auth/password"; import { isValidIP } from "@server/lib/validators"; import { isIpInCidr } from "@server/lib/ip"; -import config from "@server/lib/config"; -import { verifyExitNodeOrgAccess } from "@server/lib/exitNodes"; +import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes"; const createSiteParamsSchema = z .object({ diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 4af2feae..7a12e24a 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { deletePeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { OpenAPITags, registry } from "@server/openApi"; const deleteSiteSchema = z diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 46d3c53b..c4b3a087 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -15,7 +15,7 @@ import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; import { fromError } from "zod-validation-error"; import { z } from "zod"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export type PickSiteDefaultsResponse = { exitNodeId: number; diff --git a/server/routers/site/socketIntegration.ts b/server/routers/site/socketIntegration.ts index 34084a0a..7b5160cb 100644 --- a/server/routers/site/socketIntegration.ts +++ b/server/routers/site/socketIntegration.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import stoi from "@server/lib/stoi"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { fetchContainers, dockerSocketCache, diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index 0e958889..317f6461 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -3,6 +3,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; +import privateConfig from "#private/lib/config"; import config from "@server/lib/config"; import { db } from "@server/db"; import { count } from "drizzle-orm"; @@ -45,7 +46,7 @@ export async function isSupporterKeyVisible( } } - if (config.getRawPrivateConfig().flags?.hide_supporter_key && build != "oss") { + if (build != "oss") { visible = false; } diff --git a/server/routers/target/handleHealthcheckStatusMessage.ts b/server/routers/target/handleHealthcheckStatusMessage.ts index d726b5af..ee4e7950 100644 --- a/server/routers/target/handleHealthcheckStatusMessage.ts +++ b/server/routers/target/handleHealthcheckStatusMessage.ts @@ -1,5 +1,5 @@ import { db, targets, resources, sites, targetHealthCheck } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { Newt } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/traefik/traefikConfigProvider.ts b/server/routers/traefik/traefikConfigProvider.ts index 50ec0770..6c9404e9 100644 --- a/server/routers/traefik/traefikConfigProvider.ts +++ b/server/routers/traefik/traefikConfigProvider.ts @@ -3,7 +3,7 @@ import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { build } from "@server/build"; -import { getTraefikConfig } from "@server/lib/traefik"; +import { getTraefikConfig } from "#dynamic/lib/traefik"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; const badgerMiddlewareName = "badger"; diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index b553bd19..5e4264f9 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { verifySession } from "@server/auth/sessions/verifySession"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const acceptInviteBodySchema = z .object({ diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index b8f681c3..29f94641 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -10,11 +10,11 @@ import { db, UserOrg } from "@server/db"; import { and, eq } from "drizzle-orm"; import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db"; import { generateId } from "@server/auth/sessions/app"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 746a383b..56098bea 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -17,8 +17,8 @@ import { sendEmail } from "@server/emails"; import SendInviteLink from "@server/emails/templates/SendInviteLink"; import { OpenAPITags, registry } from "@server/openApi"; import { UserType } from "@server/types/UserTypes"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; const regenerateTracker = new NodeCache({ stdTTL: 3600, checkperiod: 600 }); diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index 6d0c5359..babccdd0 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -9,8 +9,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; import { UserType } from "@server/types/UserTypes"; diff --git a/server/routers/ws/index.ts b/server/routers/ws/index.ts index 376a960b..16440ec9 100644 --- a/server/routers/ws/index.ts +++ b/server/routers/ws/index.ts @@ -1,24 +1,2 @@ -import { build } from "@server/build"; - -// Import both modules -import * as wsModule from "./ws"; -import * as privateWsModule from "./privateWs"; - -// Conditionally export WebSocket implementation based on build type -const wsImplementation = build === "oss" ? wsModule : privateWsModule; - -// Re-export all items from the selected implementation -export const { - router, - handleWSUpgrade, - sendToClient, - broadcastToAllExcept, - connectedClients, - hasActiveConnections, - getActiveNodes, - NODE_ID, - cleanup -} = wsImplementation; - -// Re-export the MessageHandler type (both modules have the same type signature) -export type { MessageHandler } from "./privateWs"; \ No newline at end of file +export * from "./ws"; +export * from "./types"; \ No newline at end of file diff --git a/server/routers/ws/messageHandlers.ts b/server/routers/ws/messageHandlers.ts index 5b111eec..cbb023b3 100644 --- a/server/routers/ws/messageHandlers.ts +++ b/server/routers/ws/messageHandlers.ts @@ -13,10 +13,8 @@ import { handleOlmPingMessage, startOlmOfflineChecker } from "../olm"; -import { handleRemoteExitNodeRegisterMessage, handleRemoteExitNodePingMessage, startRemoteExitNodeOfflineChecker } from "@server/routers/private/remoteExitNode"; -import { MessageHandler } from "./privateWs"; import { handleHealthcheckStatusMessage } from "../target"; -import { build } from "@server/build"; +import { MessageHandler } from "./types"; export const messageHandlers: Record = { "newt/wg/register": handleNewtRegisterMessage, @@ -30,12 +28,6 @@ export const messageHandlers: Record = { "newt/ping/request": handleNewtPingRequestMessage, "newt/blueprint/apply": handleApplyBlueprintMessage, "newt/healthcheck/status": handleHealthcheckStatusMessage, - - "remoteExitNode/register": handleRemoteExitNodeRegisterMessage, - "remoteExitNode/ping": handleRemoteExitNodePingMessage, }; -startOlmOfflineChecker(); // this is to handle the offline check for olms -if (build != "oss") { - startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes -} \ No newline at end of file +startOlmOfflineChecker(); // this is to handle the offline check for olms \ No newline at end of file diff --git a/server/routers/ws/types.ts b/server/routers/ws/types.ts new file mode 100644 index 00000000..7063bc87 --- /dev/null +++ b/server/routers/ws/types.ts @@ -0,0 +1,70 @@ +import { + Newt, + newts, + NewtSession, + olms, + Olm, + OlmSession, + RemoteExitNode, + RemoteExitNodeSession, + remoteExitNodes +} from "@server/db"; +import { IncomingMessage } from "http"; +import { WebSocket } from "ws"; + +// Custom interfaces +export interface WebSocketRequest extends IncomingMessage { + token?: string; +} + +export type ClientType = "newt" | "olm" | "remoteExitNode"; + +export interface AuthenticatedWebSocket extends WebSocket { + client?: Newt | Olm | RemoteExitNode; + clientType?: ClientType; + connectionId?: string; + isFullyConnected?: boolean; + pendingMessages?: Buffer[]; +} + +export interface TokenPayload { + client: Newt | Olm | RemoteExitNode; + session: NewtSession | OlmSession | RemoteExitNodeSession; + clientType: ClientType; +} + +export interface WSMessage { + type: string; + data: any; +} + +export interface HandlerResponse { + message: WSMessage; + broadcast?: boolean; + excludeSender?: boolean; + targetClientId?: string; +} + +export interface HandlerContext { + message: WSMessage; + senderWs: WebSocket; + client: Newt | Olm | RemoteExitNode | undefined; + clientType: ClientType; + sendToClient: (clientId: string, message: WSMessage) => Promise; + broadcastToAllExcept: ( + message: WSMessage, + excludeClientId?: string + ) => Promise; + connectedClients: Map; +} + +export type MessageHandler = (context: HandlerContext) => Promise; + +// Redis message type for cross-node communication +export interface RedisMessage { + type: "direct" | "broadcast"; + targetClientId?: string; + excludeClientId?: string; + message: WSMessage; + fromNodeId: string; +} \ No newline at end of file diff --git a/server/routers/ws/ws.ts b/server/routers/ws/ws.ts index 8fb773d3..9bba41dc 100644 --- a/server/routers/ws/ws.ts +++ b/server/routers/ws/ws.ts @@ -1,7 +1,6 @@ import { Router, Request, Response } from "express"; import { Server as HttpServer } from "http"; import { WebSocket, WebSocketServer } from "ws"; -import { IncomingMessage } from "http"; import { Socket } from "net"; import { Newt, newts, NewtSession, olms, Olm, OlmSession } from "@server/db"; import { eq } from "drizzle-orm"; @@ -11,50 +10,15 @@ import { validateOlmSessionToken } from "@server/auth/sessions/olm"; import { messageHandlers } from "./messageHandlers"; import logger from "@server/logger"; import { v4 as uuidv4 } from "uuid"; +import { ClientType, TokenPayload, WebSocketRequest, WSMessage, AuthenticatedWebSocket } from "./types"; -// Custom interfaces -interface WebSocketRequest extends IncomingMessage { - token?: string; -} - -type ClientType = 'newt' | 'olm'; - -interface AuthenticatedWebSocket extends WebSocket { - client?: Newt | Olm; - clientType?: ClientType; - connectionId?: string; -} - -interface TokenPayload { +// Subset of TokenPayload for public ws.ts (newt and olm only) +interface PublicTokenPayload { client: Newt | Olm; session: NewtSession | OlmSession; - clientType: ClientType; + clientType: "newt" | "olm"; } -interface WSMessage { - type: string; - data: any; -} - -interface HandlerResponse { - message: WSMessage; - broadcast?: boolean; - excludeSender?: boolean; - targetClientId?: string; -} - -interface HandlerContext { - message: WSMessage; - senderWs: WebSocket; - client: Newt | Olm | undefined; - clientType: ClientType; - sendToClient: (clientId: string, message: WSMessage) => Promise; - broadcastToAllExcept: (message: WSMessage, excludeClientId?: string) => Promise; - connectedClients: Map; -} - -export type MessageHandler = (context: HandlerContext) => Promise; - const router: Router = Router(); const wss: WebSocketServer = new WebSocketServer({ noServer: true }); @@ -153,7 +117,7 @@ const getActiveNodes = async (clientType: ClientType, clientId: string): Promise }; // Token verification middleware -const verifyToken = async (token: string, clientType: ClientType): Promise => { +const verifyToken = async (token: string, clientType: ClientType): Promise => { try { if (clientType === 'newt') { @@ -169,7 +133,7 @@ try { return null; } return { client: existingNewt[0], session, clientType }; - } else { + } else if (clientType === 'olm') { const { session, olm } = await validateOlmSessionToken(token); if (!session || !olm) { return null; @@ -183,13 +147,15 @@ try { } return { client: existingOlm[0], session, clientType }; } + + return null; } catch (error) { logger.error("Token verification failed:", error); return null; } }; -const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, clientType: ClientType): Promise => { +const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, clientType: "newt" | "olm"): Promise => { logger.info("Establishing websocket connection"); if (!client) { logger.error("Connection attempt without client"); @@ -323,10 +289,6 @@ const cleanup = async (): Promise => { } }; -// Handle process termination -process.on('SIGTERM', cleanup); -process.on('SIGINT', cleanup); - export { router, handleWSUpgrade, diff --git a/src/app/[orgId]/layout.tsx b/src/app/[orgId]/layout.tsx index ecc7de01..d8f28a59 100644 --- a/src/app/[orgId]/layout.tsx +++ b/src/app/[orgId]/layout.tsx @@ -9,8 +9,8 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; import SetLastOrgCookie from "@app/components/SetLastOrgCookie"; -import PrivateSubscriptionStatusProvider from "@app/providers/PrivateSubscriptionStatusProvider"; -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing/getOrgSubscription"; +import PrivateSubscriptionStatusProvider from "@app/providers/SubscriptionStatusProvider"; +import { GetOrgSubscriptionResponse } from "#private/routers/billing/getOrgSubscription"; import { pullEnv } from "@app/lib/pullEnv"; import { build } from "@server/build"; diff --git a/src/app/[orgId]/settings/(private)/billing/layout.tsx b/src/app/[orgId]/settings/(private)/billing/layout.tsx index 8559ddf4..3bb60caf 100644 --- a/src/app/[orgId]/settings/(private)/billing/layout.tsx +++ b/src/app/[orgId]/settings/(private)/billing/layout.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; diff --git a/src/app/[orgId]/settings/(private)/billing/page.tsx b/src/app/[orgId]/settings/(private)/billing/page.tsx index 1270c30f..cab22311 100644 --- a/src/app/[orgId]/settings/(private)/billing/page.tsx +++ b/src/app/[orgId]/settings/(private)/billing/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@app/components/ui/button"; @@ -50,7 +37,7 @@ import { InfoPopup } from "@/components/ui/info-popup"; import { GetOrgSubscriptionResponse, GetOrgUsageResponse -} from "@server/routers/private/billing"; +} from "#private/routers/billing"; import { useTranslations } from "use-intl"; import Link from "next/link"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx index d4bfdcd0..a5be3c83 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx index 4846dd56..7cdea07a 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { GetIdpResponse as GetOrgIdpResponse } from "@server/routers/idp"; import { AxiosResponse } from "axios"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx index 1d53035c..ecc2aa83 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { redirect } from "next/navigation"; export default async function IdpPage(props: { diff --git a/src/app/[orgId]/settings/(private)/idp/create/page.tsx b/src/app/[orgId]/settings/(private)/idp/create/page.tsx index 03f83afe..ba580ca0 100644 --- a/src/app/[orgId]/settings/(private)/idp/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/create/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { diff --git a/src/app/[orgId]/settings/(private)/idp/page.tsx b/src/app/[orgId]/settings/(private)/idp/page.tsx index 1032e66c..2d1882b9 100644 --- a/src/app/[orgId]/settings/(private)/idp/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal, priv } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; @@ -22,8 +9,8 @@ import { cache } from "react"; import { GetOrgSubscriptionResponse, GetOrgTierResponse -} from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +} from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; type OrgIdpPageProps = { diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx index 4a1db4ea..a1bb69c0 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx index 11e0bcfc..c7e332d7 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx index b5835e1b..191ce3f3 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - export default function GeneralPage() { return <>; } diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx index 653444e8..bfab3086 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx @@ -1,24 +1,11 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { authCookieHeader } from "@app/lib/api/cookies"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { getTranslations } from "next-intl/server"; -import RemoteExitNodeProvider from "@app/providers/PrivateRemoteExitNodeProvider"; +import RemoteExitNodeProvider from "@app/providers/RemoteExitNodeProvider"; interface SettingsLayoutProps { children: React.ReactNode; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx index badf0971..6b39c1de 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { redirect } from "next/navigation"; export default async function RemoteExitNodePage(props: { diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx index 5daaa493..7531ba6d 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { @@ -43,7 +30,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { QuickStartRemoteExitNodeResponse, PickRemoteExitNodeDefaultsResponse -} from "@server/routers/private/remoteExitNode"; +} from "#private/routers/remoteExitNode"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter, useSearchParams } from "next/navigation"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx index b18df692..c4113558 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx @@ -1,19 +1,6 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; -import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; import { AxiosResponse } from "axios"; import ExitNodesTable, { RemoteExitNodeRow } from "./ExitNodesTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 3e6d2458..2b6fddad 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -47,8 +47,8 @@ import { ListIdpsResponse } from "@server/routers/idp"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; import Image from "next/image"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; type UserType = "internal" | "oidc"; @@ -76,7 +76,7 @@ export default function Page() { const api = createApiClient({ env }); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; const [selectedOption, setSelectedOption] = useState("internal"); diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index 31cfbc5d..6a088196 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -60,8 +60,8 @@ import { } from "@app/components/ui/select"; import { Separator } from "@app/components/ui/separator"; import { build } from "@server/build"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; const UsersRolesFormSchema = z.object({ roles: z.array( @@ -98,7 +98,7 @@ export default function ResourceAuthenticationPage() { const router = useRouter(); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; const [pageLoading, setPageLoading] = useState(true); diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 78fbfc0d..73ead11f 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -53,7 +53,7 @@ import { CreateSiteResponse, PickSiteDefaultsResponse } from "@server/routers/site"; -import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter } from "next/navigation"; diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index 0cbe101b..c438ba66 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { formatAxiosError, priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; @@ -19,13 +6,13 @@ import { verifySession } from "@app/lib/auth/verifySession"; import { redirect } from "next/navigation"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; -import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; +import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; import { build } from "@server/build"; import { headers } from "next/headers"; import { GetLoginPageResponse, LoadLoginPageResponse -} from "@server/routers/private/loginPage"; +} from "#private/routers/loginPage"; import IdpLoginButtons from "@app/components/private/IdpLoginButtons"; import { Card, @@ -37,11 +24,10 @@ import { import { Button } from "@app/components/ui/button"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; -import { GetSessionTransferTokenRenponse } from "@server/routers/auth/privateGetSessionTransferToken"; -import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; +import { GetSessionTransferTokenRenponse } from "#private/routers/auth/getSessionTransferToken"; import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken"; -import { GetOrgTierResponse } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { GetOrgTierResponse } from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 5dfa72c3..6c503da8 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -6,7 +6,7 @@ import { AxiosResponse } from "axios"; import { GetIdpResponse } from "@server/routers/idp"; import { getTranslations } from "next-intl/server"; import { pullEnv } from "@app/lib/pullEnv"; -import { LoadLoginPageResponse } from "@server/routers/private/loginPage"; +import { LoadLoginPageResponse } from "#private/routers/loginPage"; import { redirect } from "next/navigation"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index 53efef18..d37bc8ca 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -15,13 +15,13 @@ import AccessToken from "@app/components/AccessToken"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; import { ListIdpsResponse } from "@server/routers/idp"; -import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; +import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; import AutoLoginHandler from "@app/components/AutoLoginHandler"; import { build } from "@server/build"; import { headers } from "next/headers"; -import { GetLoginPageResponse } from "@server/routers/private/loginPage"; -import { GetOrgTierResponse } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { GetLoginPageResponse } from "#private/routers/loginPage"; +import { GetOrgTierResponse } from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 48170da5..29d49300 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,7 +4,7 @@ import { Inter } from "next/font/google"; import { ThemeProvider } from "@app/providers/ThemeProvider"; import EnvProvider from "@app/providers/EnvProvider"; import { pullEnv } from "@app/lib/pullEnv"; -import ThemeDataProvider from "@app/providers/PrivateThemeDataProvider"; +import ThemeDataProvider from "@app/providers/ThemeDataProvider"; import SplashImage from "@app/components/private/SplashImage"; import SupportStatusProvider from "@app/providers/SupporterStatusProvider"; import { priv } from "@app/lib/api"; diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index dccef529..20c9960c 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -32,7 +32,7 @@ import { createApiClient, formatAxiosError } from "@/lib/api"; import { useEnvContext } from "@/hooks/useEnvContext"; import { toast } from "@/hooks/useToast"; import { ListDomainsResponse } from "@server/routers/domain/listDomains"; -import { CheckDomainAvailabilityResponse } from "@server/routers/domain/privateCheckDomainNamespaceAvailability"; +import { CheckDomainAvailabilityResponse } from "#private/routers/domain/checkDomainNamespaceAvailability"; import { AxiosResponse } from "axios"; import { cn } from "@/lib/cn"; import { useTranslations } from "next-intl"; diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 1917c609..46c5ede0 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@app/components/ui/button"; import { useOrgContext } from "@app/hooks/useOrgContext"; @@ -43,7 +30,7 @@ import { SettingsSectionForm } from "@app/components/Settings"; import { useTranslations } from "next-intl"; -import { GetLoginPageResponse } from "@server/routers/private/loginPage"; +import { GetLoginPageResponse } from "#private/routers/loginPage"; import { ListDomainsResponse } from "@server/routers/domain"; import { DomainRow } from "@app/components/DomainsTable"; import { toUnicode } from "punycode"; @@ -63,8 +50,8 @@ import DomainPicker from "@app/components/DomainPicker"; import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; import { InfoPopup } from "@app/components/ui/info-popup"; import { Alert, AlertDescription } from "@app/components/ui/alert"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; // Auth page form schema @@ -94,7 +81,7 @@ const AuthPageSettings = forwardRef( const router = useRouter(); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; // Auth page domain state diff --git a/src/components/private/AutoProvisionConfigWidget.tsx b/src/components/private/AutoProvisionConfigWidget.tsx index 35800ccc..159ba01e 100644 --- a/src/components/private/AutoProvisionConfigWidget.tsx +++ b/src/components/private/AutoProvisionConfigWidget.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { diff --git a/src/components/private/CertificateStatus.tsx b/src/components/private/CertificateStatus.tsx index 1b872371..f1446806 100644 --- a/src/components/private/CertificateStatus.tsx +++ b/src/components/private/CertificateStatus.tsx @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@/components/ui/button"; import { RotateCw } from "lucide-react"; -import { useCertificate } from "@app/hooks/privateUseCertificate"; +import { useCertificate } from "@app/hooks/useCertificate"; import { useTranslations } from "next-intl"; type CertificateStatusProps = { diff --git a/src/components/private/IdpLoginButtons.tsx b/src/components/private/IdpLoginButtons.tsx index 6b3a2e0b..c2ec1f5b 100644 --- a/src/components/private/IdpLoginButtons.tsx +++ b/src/components/private/IdpLoginButtons.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEffect, useState } from "react"; diff --git a/src/components/private/OrgIdpDataTable.tsx b/src/components/private/OrgIdpDataTable.tsx index c98a6234..a7dc1850 100644 --- a/src/components/private/OrgIdpDataTable.tsx +++ b/src/components/private/OrgIdpDataTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/components/private/OrgIdpTable.tsx b/src/components/private/OrgIdpTable.tsx index f0e4d6c9..436904a0 100644 --- a/src/components/private/OrgIdpTable.tsx +++ b/src/components/private/OrgIdpTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/components/private/RegionSelector.tsx b/src/components/private/RegionSelector.tsx index 56dde743..f3928345 100644 --- a/src/components/private/RegionSelector.tsx +++ b/src/components/private/RegionSelector.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useState } from "react"; diff --git a/src/components/private/SplashImage.tsx b/src/components/private/SplashImage.tsx index a2063692..c6ddc466 100644 --- a/src/components/private/SplashImage.tsx +++ b/src/components/private/SplashImage.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/components/private/ValidateSessionTransferToken.tsx b/src/components/private/ValidateSessionTransferToken.tsx index 116785d8..99ba42f1 100644 --- a/src/components/private/ValidateSessionTransferToken.tsx +++ b/src/components/private/ValidateSessionTransferToken.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; @@ -21,7 +8,7 @@ import { useEffect, useState } from "react"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; import { useTranslations } from "next-intl"; -import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; +import { TransferSessionResponse } from "#private/routers/auth/transferSession"; type ValidateSessionTransferTokenParams = { token: string; diff --git a/src/contexts/privateRemoteExitNodeContext.ts b/src/contexts/privateRemoteExitNodeContext.ts deleted file mode 100644 index 65567b7c..00000000 --- a/src/contexts/privateRemoteExitNodeContext.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; -import { createContext } from "react"; - -type RemoteExitNodeContextType = { - remoteExitNode: GetRemoteExitNodeResponse; - updateRemoteExitNode: (updatedRemoteExitNode: Partial) => void; -}; - -const RemoteExitNodeContext = createContext(undefined); - -export default RemoteExitNodeContext; diff --git a/src/contexts/privateSubscriptionStatusContext.ts b/src/contexts/privateSubscriptionStatusContext.ts deleted file mode 100644 index aa99c21f..00000000 --- a/src/contexts/privateSubscriptionStatusContext.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; -import { createContext } from "react"; - -type SubscriptionStatusContextType = { - subscriptionStatus: GetOrgSubscriptionResponse | null; - updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; - isActive: () => boolean; - getTier: () => string | null; -}; - -const PrivateSubscriptionStatusContext = createContext< - SubscriptionStatusContextType | undefined ->(undefined); - -export default PrivateSubscriptionStatusContext; diff --git a/src/contexts/remoteExitNodeContext.ts b/src/contexts/remoteExitNodeContext.ts new file mode 100644 index 00000000..f5834e82 --- /dev/null +++ b/src/contexts/remoteExitNodeContext.ts @@ -0,0 +1,11 @@ +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; +import { createContext } from "react"; + +type RemoteExitNodeContextType = { + remoteExitNode: GetRemoteExitNodeResponse; + updateRemoteExitNode: (updatedRemoteExitNode: Partial) => void; +}; + +const RemoteExitNodeContext = createContext(undefined); + +export default RemoteExitNodeContext; diff --git a/src/contexts/subscriptionStatusContext.ts b/src/contexts/subscriptionStatusContext.ts new file mode 100644 index 00000000..267c58af --- /dev/null +++ b/src/contexts/subscriptionStatusContext.ts @@ -0,0 +1,15 @@ +import { GetOrgSubscriptionResponse } from "#private/routers/billing"; +import { createContext } from "react"; + +type SubscriptionStatusContextType = { + subscriptionStatus: GetOrgSubscriptionResponse | null; + updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; + isActive: () => boolean; + getTier: () => string | null; +}; + +const SubscriptionStatusContext = createContext< + SubscriptionStatusContextType | undefined +>(undefined); + +export default SubscriptionStatusContext; diff --git a/src/hooks/privateUseRemoteExitNodeContext.ts b/src/hooks/privateUseRemoteExitNodeContext.ts deleted file mode 100644 index 8decdb36..00000000 --- a/src/hooks/privateUseRemoteExitNodeContext.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -"use client"; - -import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; -import { build } from "@server/build"; -import { useContext } from "react"; - -export function useRemoteExitNodeContext() { - if (build == "oss") { - return null; - } - const context = useContext(RemoteExitNodeContext); - if (context === undefined) { - throw new Error("useRemoteExitNodeContext must be used within a RemoteExitNodeProvider"); - } - return context; -} diff --git a/src/hooks/privateUseSubscriptionStatusContext.ts b/src/hooks/privateUseSubscriptionStatusContext.ts deleted file mode 100644 index a6a53ac9..00000000 --- a/src/hooks/privateUseSubscriptionStatusContext.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; -import { build } from "@server/build"; -import { useContext } from "react"; - -export function usePrivateSubscriptionStatusContext() { - if (build == "oss") { - return null; - } - const context = useContext(PrivateSubscriptionStatusContext); - if (context === undefined) { - throw new Error( - "usePrivateSubscriptionStatusContext must be used within an PrivateSubscriptionStatusProvider" - ); - } - return context; -} diff --git a/src/hooks/privateUseCertificate.ts b/src/hooks/useCertificate.ts similarity index 88% rename from src/hooks/privateUseCertificate.ts rename to src/hooks/useCertificate.ts index 7956c3c7..b3c66080 100644 --- a/src/hooks/privateUseCertificate.ts +++ b/src/hooks/useCertificate.ts @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useState, useCallback, useEffect } from "react"; import { AxiosResponse } from "axios"; -import { GetCertificateResponse } from "@server/routers/private/certificates"; +import { GetCertificateResponse } from "#private/routers/certificates"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/hooks/useRemoteExitNodeContext.ts b/src/hooks/useRemoteExitNodeContext.ts new file mode 100644 index 00000000..486147c4 --- /dev/null +++ b/src/hooks/useRemoteExitNodeContext.ts @@ -0,0 +1,16 @@ +"use client"; + +import RemoteExitNodeContext from "@app/contexts/remoteExitNodeContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function useRemoteExitNodeContext() { + if (build == "oss") { + return null; + } + const context = useContext(RemoteExitNodeContext); + if (context === undefined) { + throw new Error("useRemoteExitNodeContext must be used within a RemoteExitNodeProvider"); + } + return context; +} diff --git a/src/hooks/useSubscriptionStatusContext.ts b/src/hooks/useSubscriptionStatusContext.ts new file mode 100644 index 00000000..240168b1 --- /dev/null +++ b/src/hooks/useSubscriptionStatusContext.ts @@ -0,0 +1,16 @@ +import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function useSubscriptionStatusContext() { + if (build == "oss") { + return null; + } + const context = useContext(SubscriptionStatusContext); + if (context === undefined) { + throw new Error( + "useSubscriptionStatusContext must be used within an SubscriptionStatusProvider" + ); + } + return context; +} diff --git a/src/lib/privateThemeColors.ts b/src/lib/themeColors.ts similarity index 87% rename from src/lib/privateThemeColors.ts rename to src/lib/themeColors.ts index 0543d68a..7c6bb46e 100644 --- a/src/lib/privateThemeColors.ts +++ b/src/lib/themeColors.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - const defaultTheme = { light: { background: "oklch(0.99 0 0)", diff --git a/src/providers/PrivateRemoteExitNodeProvider.tsx b/src/providers/RemoteExitNodeProvider.tsx similarity index 67% rename from src/providers/PrivateRemoteExitNodeProvider.tsx rename to src/providers/RemoteExitNodeProvider.tsx index 8dfd8f1a..72cfd107 100644 --- a/src/providers/PrivateRemoteExitNodeProvider.tsx +++ b/src/providers/RemoteExitNodeProvider.tsx @@ -1,20 +1,7 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import RemoteExitNodeContext from "@app/contexts/remoteExitNodeContext"; +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; import { useState } from "react"; import { useTranslations } from "next-intl"; diff --git a/src/providers/PrivateSubscriptionStatusProvider.tsx b/src/providers/SubscriptionStatusProvider.tsx similarity index 72% rename from src/providers/PrivateSubscriptionStatusProvider.tsx rename to src/providers/SubscriptionStatusProvider.tsx index 9f4c5cd2..71a9401c 100644 --- a/src/providers/PrivateSubscriptionStatusProvider.tsx +++ b/src/providers/SubscriptionStatusProvider.tsx @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; -import { getTierPriceSet } from "@server/lib/private/billing/tiers"; -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; +import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; +import { getTierPriceSet } from "@server/lib/billing/tiers"; +import { GetOrgSubscriptionResponse } from "#private/routers/billing"; import { useState } from "react"; interface ProviderProps { @@ -68,7 +55,7 @@ export function PrivateSubscriptionStatusProvider({ }; return ( - {children} - + ); } diff --git a/src/providers/PrivateThemeDataProvider.tsx b/src/providers/ThemeDataProvider.tsx similarity index 67% rename from src/providers/PrivateThemeDataProvider.tsx rename to src/providers/ThemeDataProvider.tsx index a8da4551..821f9248 100644 --- a/src/providers/PrivateThemeDataProvider.tsx +++ b/src/providers/ThemeDataProvider.tsx @@ -1,19 +1,6 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import setGlobalColorTheme from "@app/lib/privateThemeColors"; +import setGlobalColorTheme from "@app/lib/themeColors"; import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; diff --git a/tsconfig.json b/tsconfig.json index 7af98e53..e32eabd3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,11 @@ "@test/*": ["../test/*"], "@app/*": ["*"], "@cli/*": ["../cli/*"], - "@/*": ["./*"] + "@/*": ["./*"], + "#private/*": ["../server/private/*"], + "#open/*": ["../server/*"], + "#closed/*": ["../server/private/*"], + "#dynamic/*": ["../server/*"] }, "plugins": [ {