mirror of
https://github.com/fosrl/pangolin.git
synced 2025-06-20 20:35:43 +02:00
support postgresql as database option
This commit is contained in:
parent
62a0104e70
commit
2cca561e51
218 changed files with 1417 additions and 713 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -32,4 +32,4 @@ installer
|
||||||
bin
|
bin
|
||||||
.secrets
|
.secrets
|
||||||
test_event.json
|
test_event.json
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -8,9 +8,11 @@ RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schemas/ --out init
|
RUN echo 'export * from "./pg";' > server/db/index.ts
|
||||||
|
|
||||||
RUN npm run build
|
RUN npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init
|
||||||
|
|
||||||
|
RUN npm run build:pg
|
||||||
|
|
||||||
FROM node:20-alpine AS runner
|
FROM node:20-alpine AS runner
|
||||||
|
|
||||||
|
@ -32,4 +34,4 @@ COPY server/db/names.json ./dist/names.json
|
||||||
|
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "run", "start:pg"]
|
6
Makefile
6
Makefile
|
@ -3,8 +3,10 @@ build-release:
|
||||||
echo "Error: tag is required. Usage: make build-all tag=<tag>"; \
|
echo "Error: tag is required. Usage: make build-all tag=<tag>"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest -f Dockerfile --push .
|
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest -f Dockerfile.sqlite --push .
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) -f Dockerfile --push .
|
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) -f Dockerfile.sqlite --push .
|
||||||
|
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-latest -f Dockerfile.pg --push .
|
||||||
|
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(tag) -f Dockerfile.pg --push .
|
||||||
|
|
||||||
build-arm:
|
build-arm:
|
||||||
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
||||||
|
|
12
drizzle.pg.config.ts
Normal file
12
drizzle.pg.config.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
dialect: "postgresql",
|
||||||
|
schema: path.join("server", "db", "pg", "schema.ts"),
|
||||||
|
out: path.join("server", "migrations"),
|
||||||
|
verbose: true,
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.DATABASE_URL as string
|
||||||
|
}
|
||||||
|
});
|
|
@ -4,7 +4,7 @@ import path from "path";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
schema: path.join("server", "db", "schemas"),
|
schema: path.join("server", "db", "sqlite", "schema.ts"),
|
||||||
out: path.join("server", "migrations"),
|
out: path.join("server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
BIN
newt
BIN
newt
Binary file not shown.
|
@ -1,7 +1,7 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true,
|
ignoreDuringBuilds: true
|
||||||
},
|
},
|
||||||
output: "standalone"
|
output: "standalone"
|
||||||
};
|
};
|
||||||
|
|
147
package-lock.json
generated
147
package-lock.json
generated
|
@ -69,6 +69,7 @@
|
||||||
"nodemailer": "6.9.16",
|
"nodemailer": "6.9.16",
|
||||||
"npm": "^11.2.0",
|
"npm": "^11.2.0",
|
||||||
"oslo": "1.2.1",
|
"oslo": "1.2.1",
|
||||||
|
"pg": "^8.16.0",
|
||||||
"qrcode.react": "4.2.0",
|
"qrcode.react": "4.2.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
|
@ -12512,6 +12513,95 @@
|
||||||
"url": "https://ko-fi.com/killymxi"
|
"url": "https://ko-fi.com/killymxi"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pg": {
|
||||||
|
"version": "8.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz",
|
||||||
|
"integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-connection-string": "^2.9.0",
|
||||||
|
"pg-pool": "^3.10.0",
|
||||||
|
"pg-protocol": "^1.10.0",
|
||||||
|
"pg-types": "2.2.0",
|
||||||
|
"pgpass": "1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"pg-cloudflare": "^1.2.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg-native": ">=3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"pg-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-cloudflare": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/pg-connection-string": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-int8": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-pool": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz",
|
||||||
|
"integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-protocol": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-types": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"postgres-array": "~2.0.0",
|
||||||
|
"postgres-bytea": "~1.0.0",
|
||||||
|
"postgres-date": "~1.0.4",
|
||||||
|
"postgres-interval": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
@ -12582,6 +12672,45 @@
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postgres-array": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-bytea": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-date": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-interval": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prebuild-install": {
|
"node_modules/prebuild-install": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||||
|
@ -14316,6 +14445,15 @@
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stable-hash": {
|
"node_modules/stable-hash": {
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
|
||||||
|
@ -15498,6 +15636,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|
14
package.json
14
package.json
|
@ -12,11 +12,16 @@
|
||||||
"license": "SEE LICENSE IN LICENSE AND README.md",
|
"license": "SEE LICENSE IN LICENSE AND README.md",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_ENV=development ENVIRONMENT=dev tsx watch server/index.ts",
|
"dev": "NODE_ENV=development ENVIRONMENT=dev tsx watch server/index.ts",
|
||||||
"db:generate": "drizzle-kit generate",
|
"db:pg:generate": "drizzle-kit generate --config=./drizzle.pg.config.ts",
|
||||||
"db:push": "npx tsx server/db/migrate.ts",
|
"db:sqlite:generate": "drizzle-kit generate --config=./drizzle.sqlite.config.ts",
|
||||||
|
"db:pg:push": "npx tsx server/db/pg/migrate.ts",
|
||||||
|
"db:sqlite:push": "npx tsx server/db/sqlite/migrate.ts",
|
||||||
"db:studio": "drizzle-kit studio",
|
"db:studio": "drizzle-kit studio",
|
||||||
"build": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrations.ts -o dist/migrations.mjs",
|
"db:clear-migrations": "rm -rf server/migrations",
|
||||||
"start": "NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
|
"build:sqlite": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs",
|
||||||
|
"build:pg": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs",
|
||||||
|
"start:sqlite": "DB_TYPE=sqlite NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
|
||||||
|
"start:pg": "DB_TYPE=pg NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
|
||||||
"email": "email dev --dir server/emails/templates --port 3005"
|
"email": "email dev --dir server/emails/templates --port 3005"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -80,6 +85,7 @@
|
||||||
"nodemailer": "6.9.16",
|
"nodemailer": "6.9.16",
|
||||||
"npm": "^11.2.0",
|
"npm": "^11.2.0",
|
||||||
"oslo": "1.2.1",
|
"oslo": "1.2.1",
|
||||||
|
"pg": "^8.16.0",
|
||||||
"qrcode.react": "4.2.0",
|
"qrcode.react": "4.2.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userActions, roleActions, userOrgs } from "@server/db/schemas";
|
import { userActions, roleActions, userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { roleResources, userResources } from "@server/db/schemas";
|
import { roleResources, userResources } from "@server/db";
|
||||||
|
|
||||||
export async function canUserAccessResource({
|
export async function canUserAccessResource({
|
||||||
userId,
|
userId,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { UserInvite, userInvites } from "@server/db/schemas";
|
import { UserInvite, userInvites } from "@server/db";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import { verifyPassword } from "./password";
|
import { verifyPassword } from "./password";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from '@server/db';
|
import { db } from '@server/db';
|
||||||
import { limitsTable } from '@server/db/schemas';
|
import { limitsTable } from '@server/db';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceOtp } from "@server/db/schemas";
|
import { resourceOtp } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { createDate, isWithinExpirationDate, TimeSpan } from "oslo";
|
import { createDate, isWithinExpirationDate, TimeSpan } from "oslo";
|
||||||
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { TimeSpan, createDate } from "oslo";
|
import { TimeSpan, createDate } from "oslo";
|
||||||
import { generateRandomString, alphabet } from "oslo/crypto";
|
import { generateRandomString, alphabet } from "oslo/crypto";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users, emailVerificationCodes } from "@server/db/schemas";
|
import { users, emailVerificationCodes } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { sendEmail } from "@server/emails";
|
import { sendEmail } from "@server/emails";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
|
@ -9,8 +9,8 @@ import {
|
||||||
sessions,
|
sessions,
|
||||||
User,
|
User,
|
||||||
users
|
users
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { eq, inArray } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import type { RandomReader } from "@oslojs/crypto/random";
|
import type { RandomReader } from "@oslojs/crypto/random";
|
||||||
|
|
|
@ -2,8 +2,8 @@ import {
|
||||||
encodeHexLowerCase,
|
encodeHexLowerCase,
|
||||||
} from "@oslojs/encoding";
|
} from "@oslojs/encoding";
|
||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import { Newt, newts, newtSessions, NewtSession } from "@server/db/schemas";
|
import { Newt, newts, newtSessions, NewtSession } from "@server/db";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
export const EXPIRES = 1000 * 60 * 60 * 24 * 30;
|
export const EXPIRES = 1000 * 60 * 60 * 24 * 30;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { encodeHexLowerCase } from "@oslojs/encoding";
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import { resourceSessions, ResourceSession } from "@server/db/schemas";
|
import { resourceSessions, ResourceSession } from "@server/db";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { verify } from "@node-rs/argon2";
|
import { verify } from "@node-rs/argon2";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { twoFactorBackupCodes } from "@server/db/schemas";
|
import { twoFactorBackupCodes } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { decodeHex } from "oslo/encoding";
|
import { decodeHex } from "oslo/encoding";
|
||||||
import { TOTPController } from "oslo/otp";
|
import { TOTPController } from "oslo/otp";
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import {
|
import {
|
||||||
Resource,
|
Resource,
|
||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
resources
|
resources
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import { verifyPassword } from "./password";
|
import { verifyPassword } from "./password";
|
||||||
|
|
66
server/db/README.md
Normal file
66
server/db/README.md
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# Database
|
||||||
|
|
||||||
|
Pangolin can use a Postgres or SQLite database to store its data.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Postgres
|
||||||
|
|
||||||
|
To use Postgres, edit `server/db/index.ts` to export all from `server/db/pg/index.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export * from "./pg";
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you have a valid config file with a connection string:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
postgres:
|
||||||
|
connection_string: postgresql://postgres:postgres@localhost:5432
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run an ephemeral Postgres database for local development using Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name postgres \
|
||||||
|
--rm \
|
||||||
|
-p 5432:5432 \
|
||||||
|
-e POSTGRES_PASSWORD=postgres \
|
||||||
|
-v $(mktemp -d):/var/lib/postgresql/data \
|
||||||
|
postgres:17
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQLite
|
||||||
|
|
||||||
|
To use SQLite, edit `server/db/index.ts` to export all from `server/db/sqlite/index.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export * from "./sqlite";
|
||||||
|
```
|
||||||
|
|
||||||
|
No edits to the config are needed. If you keep the Postgres config, it will be ignored.
|
||||||
|
|
||||||
|
## Generate and Push Migrations
|
||||||
|
|
||||||
|
Ensure drizzle-kit is installed.
|
||||||
|
|
||||||
|
### Postgres
|
||||||
|
|
||||||
|
You must have a connection string in your config file, as shown above.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:pg:generate
|
||||||
|
npm run db:pg:push
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQLite
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:sqlite:generate
|
||||||
|
npm run db:sqlite:push
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Time
|
||||||
|
|
||||||
|
There is a dockerfile for each database type. The dockerfile swaps out the `server/db/index.ts` file to use the correct database type.
|
|
@ -1,63 +1,2 @@
|
||||||
import { drizzle as DrizzleSqlite } from "drizzle-orm/better-sqlite3";
|
export * from "./sqlite";
|
||||||
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
// export * from "./pg";
|
||||||
import Database from "better-sqlite3";
|
|
||||||
import * as schema from "@server/db/schemas";
|
|
||||||
import path from "path";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import { APP_PATH } from "@server/lib/consts";
|
|
||||||
import { existsSync, mkdirSync } from "fs";
|
|
||||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
|
||||||
|
|
||||||
export const location = path.join(APP_PATH, "db", "db.sqlite");
|
|
||||||
export const exists = await checkFileExists(location);
|
|
||||||
|
|
||||||
bootstrapVolume();
|
|
||||||
|
|
||||||
function createDb() {
|
|
||||||
const config = readConfigFile();
|
|
||||||
|
|
||||||
if (config.database.type === "postgres") {
|
|
||||||
return DrizzlePostgres(config.database!.postgres!.connection_string!);
|
|
||||||
} else {
|
|
||||||
const sqlite = new Database(location);
|
|
||||||
return DrizzleSqlite(sqlite, { schema });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const db = createDb();
|
|
||||||
export default db;
|
|
||||||
|
|
||||||
async function checkFileExists(filePath: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
await fs.access(filePath);
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function bootstrapVolume() {
|
|
||||||
const appPath = APP_PATH;
|
|
||||||
|
|
||||||
const dbDir = path.join(appPath, "db");
|
|
||||||
const logsDir = path.join(appPath, "logs");
|
|
||||||
|
|
||||||
// check if the db directory exists and create it if it doesn't
|
|
||||||
if (!existsSync(dbDir)) {
|
|
||||||
mkdirSync(dbDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the logs directory exists and create it if it doesn't
|
|
||||||
if (!existsSync(logsDir)) {
|
|
||||||
mkdirSync(logsDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// THIS IS FOR TRAEFIK; NOT REALLY NEEDED, BUT JUST IN CASE
|
|
||||||
|
|
||||||
const traefikDir = path.join(appPath, "traefik");
|
|
||||||
|
|
||||||
// check if the traefik directory exists and create it if it doesn't
|
|
||||||
if (!existsSync(traefikDir)) {
|
|
||||||
mkdirSync(traefikDir, { recursive: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { exitNodes, sites } from "./schemas/schema";
|
import { exitNodes, sites } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { __DIRNAME } from "@server/lib/consts";
|
import { __DIRNAME } from "@server/lib/consts";
|
||||||
|
|
||||||
|
|
17
server/db/pg/driver.ts
Normal file
17
server/db/pg/driver.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
||||||
|
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||||
|
|
||||||
|
function createDb() {
|
||||||
|
const config = readConfigFile();
|
||||||
|
|
||||||
|
const connectionString = config.postgres?.connection_string;
|
||||||
|
|
||||||
|
if (!connectionString) {
|
||||||
|
throw new Error("Postgres connection string is not defined in the configuration file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return DrizzlePostgres(connectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const db = createDb();
|
||||||
|
export default db;
|
|
@ -1 +1,2 @@
|
||||||
|
export * from "./driver";
|
||||||
export * from "./schema";
|
export * from "./schema";
|
|
@ -1,5 +1,5 @@
|
||||||
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
||||||
import db from "@server/db";
|
import db from "./driver";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const migrationsFolder = path.join("server/migrations");
|
const migrationsFolder = path.join("server/migrations");
|
||||||
|
@ -7,7 +7,7 @@ const migrationsFolder = path.join("server/migrations");
|
||||||
const runMigrations = async () => {
|
const runMigrations = async () => {
|
||||||
console.log("Running migrations...");
|
console.log("Running migrations...");
|
||||||
try {
|
try {
|
||||||
migrate(db as any, {
|
await migrate(db as any, {
|
||||||
migrationsFolder: migrationsFolder
|
migrationsFolder: migrationsFolder
|
||||||
});
|
});
|
||||||
console.log("Migrations completed successfully.");
|
console.log("Migrations completed successfully.");
|
531
server/db/pg/schema.ts
Normal file
531
server/db/pg/schema.ts
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
import {
|
||||||
|
pgTable,
|
||||||
|
serial,
|
||||||
|
varchar,
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
bigint,
|
||||||
|
real
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { InferSelectModel } from "drizzle-orm";
|
||||||
|
|
||||||
|
export const domains = pgTable("domains", {
|
||||||
|
domainId: varchar("domainId").primaryKey(),
|
||||||
|
baseDomain: varchar("baseDomain").notNull(),
|
||||||
|
configManaged: boolean("configManaged").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const orgs = pgTable("orgs", {
|
||||||
|
orgId: varchar("orgId").primaryKey(),
|
||||||
|
name: varchar("name").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const orgDomains = pgTable("orgDomains", {
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
domainId: varchar("domainId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => domains.domainId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sites = pgTable("sites", {
|
||||||
|
siteId: serial("siteId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
niceId: varchar("niceId").notNull(),
|
||||||
|
exitNodeId: integer("exitNode").references(() => exitNodes.exitNodeId, {
|
||||||
|
onDelete: "set null"
|
||||||
|
}),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
pubKey: varchar("pubKey"),
|
||||||
|
subnet: varchar("subnet").notNull(),
|
||||||
|
megabytesIn: real("bytesIn"),
|
||||||
|
megabytesOut: real("bytesOut"),
|
||||||
|
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
||||||
|
type: varchar("type").notNull(), // "newt" or "wireguard"
|
||||||
|
online: boolean("online").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resources = pgTable("resources", {
|
||||||
|
resourceId: serial("resourceId").primaryKey(),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.references(() => sites.siteId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
subdomain: varchar("subdomain"),
|
||||||
|
fullDomain: varchar("fullDomain"),
|
||||||
|
domainId: varchar("domainId").references(() => domains.domainId, {
|
||||||
|
onDelete: "set null"
|
||||||
|
}),
|
||||||
|
ssl: boolean("ssl").notNull().default(false),
|
||||||
|
blockAccess: boolean("blockAccess").notNull().default(false),
|
||||||
|
sso: boolean("sso").notNull().default(true),
|
||||||
|
http: boolean("http").notNull().default(true),
|
||||||
|
protocol: varchar("protocol").notNull(),
|
||||||
|
proxyPort: integer("proxyPort"),
|
||||||
|
emailWhitelistEnabled: boolean("emailWhitelistEnabled")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
|
isBaseDomain: boolean("isBaseDomain"),
|
||||||
|
applyRules: boolean("applyRules").notNull().default(false),
|
||||||
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
|
stickySession: boolean("stickySession").notNull().default(false),
|
||||||
|
tlsServerName: varchar("tlsServerName"),
|
||||||
|
setHostHeader: varchar("setHostHeader")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const targets = pgTable("targets", {
|
||||||
|
targetId: serial("targetId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.references(() => resources.resourceId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
ip: varchar("ip").notNull(),
|
||||||
|
method: varchar("method"),
|
||||||
|
port: integer("port").notNull(),
|
||||||
|
internalPort: integer("internalPort"),
|
||||||
|
enabled: boolean("enabled").notNull().default(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const exitNodes = pgTable("exitNodes", {
|
||||||
|
exitNodeId: serial("exitNodeId").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
address: varchar("address").notNull(),
|
||||||
|
endpoint: varchar("endpoint").notNull(),
|
||||||
|
publicKey: varchar("publicKey").notNull(),
|
||||||
|
listenPort: integer("listenPort").notNull(),
|
||||||
|
reachableAt: varchar("reachableAt")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const users = pgTable("user", {
|
||||||
|
userId: varchar("id").primaryKey(),
|
||||||
|
email: varchar("email"),
|
||||||
|
username: varchar("username").notNull(),
|
||||||
|
name: varchar("name"),
|
||||||
|
type: varchar("type").notNull(), // "internal", "oidc"
|
||||||
|
idpId: integer("idpId").references(() => idp.idpId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
passwordHash: varchar("passwordHash"),
|
||||||
|
twoFactorEnabled: boolean("twoFactorEnabled").notNull().default(false),
|
||||||
|
twoFactorSecret: varchar("twoFactorSecret"),
|
||||||
|
emailVerified: boolean("emailVerified").notNull().default(false),
|
||||||
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
|
serverAdmin: boolean("serverAdmin").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const newts = pgTable("newt", {
|
||||||
|
newtId: varchar("id").primaryKey(),
|
||||||
|
secretHash: varchar("secretHash").notNull(),
|
||||||
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
|
siteId: integer("siteId").references(() => sites.siteId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const twoFactorBackupCodes = pgTable("twoFactorBackupCodes", {
|
||||||
|
codeId: serial("id").primaryKey(),
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
codeHash: varchar("codeHash").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sessions = pgTable("session", {
|
||||||
|
sessionId: varchar("id").primaryKey(),
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const newtSessions = pgTable("newtSession", {
|
||||||
|
sessionId: varchar("id").primaryKey(),
|
||||||
|
newtId: varchar("newtId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => newts.newtId, { onDelete: "cascade" }),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const userOrgs = pgTable("userOrgs", {
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
roleId: integer("roleId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.roleId),
|
||||||
|
isOwner: boolean("isOwner").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
|
||||||
|
codeId: serial("id").primaryKey(),
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
code: varchar("code").notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const passwordResetTokens = pgTable("passwordResetTokens", {
|
||||||
|
tokenId: serial("id").primaryKey(),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
tokenHash: varchar("tokenHash").notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const actions = pgTable("actions", {
|
||||||
|
actionId: varchar("actionId").primaryKey(),
|
||||||
|
name: varchar("name"),
|
||||||
|
description: varchar("description")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const roles = pgTable("roles", {
|
||||||
|
roleId: serial("roleId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
isAdmin: boolean("isAdmin"),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
description: varchar("description")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const roleActions = pgTable("roleActions", {
|
||||||
|
roleId: integer("roleId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
|
actionId: varchar("actionId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const userActions = pgTable("userActions", {
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
actionId: varchar("actionId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const roleSites = pgTable("roleSites", {
|
||||||
|
roleId: integer("roleId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sites.siteId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const userSites = pgTable("userSites", {
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sites.siteId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const roleResources = pgTable("roleResources", {
|
||||||
|
roleId: integer("roleId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const userResources = pgTable("userResources", {
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const limitsTable = pgTable("limits", {
|
||||||
|
limitId: serial("limitId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
value: bigint("value", { mode: "number" }).notNull(),
|
||||||
|
description: varchar("description")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const userInvites = pgTable("userInvites", {
|
||||||
|
inviteId: varchar("inviteId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||||
|
tokenHash: varchar("token").notNull(),
|
||||||
|
roleId: integer("roleId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourcePincode = pgTable("resourcePincode", {
|
||||||
|
pincodeId: serial("pincodeId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
pincodeHash: varchar("pincodeHash").notNull(),
|
||||||
|
digitLength: integer("digitLength").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourcePassword = pgTable("resourcePassword", {
|
||||||
|
passwordId: serial("passwordId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
passwordHash: varchar("passwordHash").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourceAccessToken = pgTable("resourceAccessToken", {
|
||||||
|
accessTokenId: varchar("accessTokenId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
tokenHash: varchar("tokenHash").notNull(),
|
||||||
|
sessionLength: bigint("sessionLength", { mode: "number" }).notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }),
|
||||||
|
title: varchar("title"),
|
||||||
|
description: varchar("description"),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourceSessions = pgTable("resourceSessions", {
|
||||||
|
sessionId: varchar("id").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||||
|
sessionLength: bigint("sessionLength", { mode: "number" }).notNull(),
|
||||||
|
doNotExtend: boolean("doNotExtend").notNull().default(false),
|
||||||
|
isRequestToken: boolean("isRequestToken"),
|
||||||
|
userSessionId: varchar("userSessionId").references(
|
||||||
|
() => sessions.sessionId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
passwordId: integer("passwordId").references(
|
||||||
|
() => resourcePassword.passwordId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
pincodeId: integer("pincodeId").references(
|
||||||
|
() => resourcePincode.pincodeId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
whitelistId: integer("whitelistId").references(
|
||||||
|
() => resourceWhitelist.whitelistId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
accessTokenId: varchar("accessTokenId").references(
|
||||||
|
() => resourceAccessToken.accessTokenId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourceWhitelist = pgTable("resourceWhitelist", {
|
||||||
|
whitelistId: serial("id").primaryKey(),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourceOtp = pgTable("resourceOtp", {
|
||||||
|
otpId: serial("otpId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
otpHash: varchar("otpHash").notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const versionMigrations = pgTable("versionMigrations", {
|
||||||
|
version: varchar("version").primaryKey(),
|
||||||
|
executedAt: bigint("executedAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resourceRules = pgTable("resourceRules", {
|
||||||
|
ruleId: serial("ruleId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
|
priority: integer("priority").notNull(),
|
||||||
|
action: varchar("action").notNull(), // ACCEPT, DROP
|
||||||
|
match: varchar("match").notNull(), // CIDR, PATH, IP
|
||||||
|
value: varchar("value").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const supporterKey = pgTable("supporterKey", {
|
||||||
|
keyId: serial("keyId").primaryKey(),
|
||||||
|
key: varchar("key").notNull(),
|
||||||
|
githubUsername: varchar("githubUsername").notNull(),
|
||||||
|
phrase: varchar("phrase"),
|
||||||
|
tier: varchar("tier"),
|
||||||
|
valid: boolean("valid").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const idp = pgTable("idp", {
|
||||||
|
idpId: serial("idpId").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
type: varchar("type").notNull(),
|
||||||
|
defaultRoleMapping: varchar("defaultRoleMapping"),
|
||||||
|
defaultOrgMapping: varchar("defaultOrgMapping"),
|
||||||
|
autoProvision: boolean("autoProvision").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const idpOidcConfig = pgTable("idpOidcConfig", {
|
||||||
|
idpOauthConfigId: serial("idpOauthConfigId").primaryKey(),
|
||||||
|
idpId: integer("idpId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => idp.idpId, { onDelete: "cascade" }),
|
||||||
|
clientId: varchar("clientId").notNull(),
|
||||||
|
clientSecret: varchar("clientSecret").notNull(),
|
||||||
|
authUrl: varchar("authUrl").notNull(),
|
||||||
|
tokenUrl: varchar("tokenUrl").notNull(),
|
||||||
|
identifierPath: varchar("identifierPath").notNull(),
|
||||||
|
emailPath: varchar("emailPath"),
|
||||||
|
namePath: varchar("namePath"),
|
||||||
|
scopes: varchar("scopes").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const licenseKey = pgTable("licenseKey", {
|
||||||
|
licenseKeyId: varchar("licenseKeyId").primaryKey().notNull(),
|
||||||
|
instanceId: varchar("instanceId").notNull(),
|
||||||
|
token: varchar("token").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const hostMeta = pgTable("hostMeta", {
|
||||||
|
hostMetaId: varchar("hostMetaId").primaryKey().notNull(),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiKeys = pgTable("apiKeys", {
|
||||||
|
apiKeyId: varchar("apiKeyId").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
apiKeyHash: varchar("apiKeyHash").notNull(),
|
||||||
|
lastChars: varchar("lastChars").notNull(),
|
||||||
|
createdAt: varchar("dateCreated").notNull(),
|
||||||
|
isRoot: boolean("isRoot").notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiKeyActions = pgTable("apiKeyActions", {
|
||||||
|
apiKeyId: varchar("apiKeyId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
||||||
|
actionId: varchar("actionId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => actions.actionId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiKeyOrg = pgTable("apiKeyOrg", {
|
||||||
|
apiKeyId: varchar("apiKeyId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const idpOrg = pgTable("idpOrg", {
|
||||||
|
idpId: integer("idpId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => idp.idpId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
roleMapping: varchar("roleMapping"),
|
||||||
|
orgMapping: varchar("orgMapping")
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Org = InferSelectModel<typeof orgs>;
|
||||||
|
export type User = InferSelectModel<typeof users>;
|
||||||
|
export type Site = InferSelectModel<typeof sites>;
|
||||||
|
export type Resource = InferSelectModel<typeof resources>;
|
||||||
|
export type ExitNode = InferSelectModel<typeof exitNodes>;
|
||||||
|
export type Target = InferSelectModel<typeof targets>;
|
||||||
|
export type Session = InferSelectModel<typeof sessions>;
|
||||||
|
export type Newt = InferSelectModel<typeof newts>;
|
||||||
|
export type NewtSession = InferSelectModel<typeof newtSessions>;
|
||||||
|
export type EmailVerificationCode = InferSelectModel<
|
||||||
|
typeof emailVerificationCodes
|
||||||
|
>;
|
||||||
|
export type TwoFactorBackupCode = InferSelectModel<typeof twoFactorBackupCodes>;
|
||||||
|
export type PasswordResetToken = InferSelectModel<typeof passwordResetTokens>;
|
||||||
|
export type Role = InferSelectModel<typeof roles>;
|
||||||
|
export type Action = InferSelectModel<typeof actions>;
|
||||||
|
export type RoleAction = InferSelectModel<typeof roleActions>;
|
||||||
|
export type UserAction = InferSelectModel<typeof userActions>;
|
||||||
|
export type RoleSite = InferSelectModel<typeof roleSites>;
|
||||||
|
export type UserSite = InferSelectModel<typeof userSites>;
|
||||||
|
export type RoleResource = InferSelectModel<typeof roleResources>;
|
||||||
|
export type UserResource = InferSelectModel<typeof userResources>;
|
||||||
|
export type Limit = InferSelectModel<typeof limitsTable>;
|
||||||
|
export type UserInvite = InferSelectModel<typeof userInvites>;
|
||||||
|
export type UserOrg = InferSelectModel<typeof userOrgs>;
|
||||||
|
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
||||||
|
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||||
|
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||||
|
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
||||||
|
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
||||||
|
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
||||||
|
export type VersionMigration = InferSelectModel<typeof versionMigrations>;
|
||||||
|
export type ResourceRule = InferSelectModel<typeof resourceRules>;
|
||||||
|
export type Domain = InferSelectModel<typeof domains>;
|
||||||
|
export type SupporterKey = InferSelectModel<typeof supporterKey>;
|
||||||
|
export type Idp = InferSelectModel<typeof idp>;
|
||||||
|
export type ApiKey = InferSelectModel<typeof apiKeys>;
|
||||||
|
export type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
|
||||||
|
export type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
|
58
server/db/sqlite/driver.ts
Normal file
58
server/db/sqlite/driver.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { drizzle as DrizzleSqlite } from "drizzle-orm/better-sqlite3";
|
||||||
|
import Database from "better-sqlite3";
|
||||||
|
import * as schema from "./schema";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import { APP_PATH } from "@server/lib/consts";
|
||||||
|
import { existsSync, mkdirSync } from "fs";
|
||||||
|
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||||
|
|
||||||
|
export const location = path.join(APP_PATH, "db", "db.sqlite");
|
||||||
|
export const exists = await checkFileExists(location);
|
||||||
|
|
||||||
|
bootstrapVolume();
|
||||||
|
|
||||||
|
function createDb() {
|
||||||
|
const config = readConfigFile();
|
||||||
|
|
||||||
|
const sqlite = new Database(location);
|
||||||
|
return DrizzleSqlite(sqlite, { schema });
|
||||||
|
}
|
||||||
|
|
||||||
|
export const db = createDb();
|
||||||
|
export default db;
|
||||||
|
|
||||||
|
async function checkFileExists(filePath: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await fs.access(filePath);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootstrapVolume() {
|
||||||
|
const appPath = APP_PATH;
|
||||||
|
|
||||||
|
const dbDir = path.join(appPath, "db");
|
||||||
|
const logsDir = path.join(appPath, "logs");
|
||||||
|
|
||||||
|
// check if the db directory exists and create it if it doesn't
|
||||||
|
if (!existsSync(dbDir)) {
|
||||||
|
mkdirSync(dbDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the logs directory exists and create it if it doesn't
|
||||||
|
if (!existsSync(logsDir)) {
|
||||||
|
mkdirSync(logsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS IS FOR TRAEFIK; NOT REALLY NEEDED, BUT JUST IN CASE
|
||||||
|
|
||||||
|
const traefikDir = path.join(appPath, "traefik");
|
||||||
|
|
||||||
|
// check if the traefik directory exists and create it if it doesn't
|
||||||
|
if (!existsSync(traefikDir)) {
|
||||||
|
mkdirSync(traefikDir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
2
server/db/sqlite/index.ts
Normal file
2
server/db/sqlite/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./driver";
|
||||||
|
export * from "./schema";
|
|
@ -1,5 +1,5 @@
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
import db from "@server/db";
|
import db from "./driver";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const migrationsFolder = path.join("server/migrations");
|
const migrationsFolder = path.join("server/migrations");
|
|
@ -4,7 +4,7 @@ import { runSetupFunctions } from "./setup";
|
||||||
import { createApiServer } from "./apiServer";
|
import { createApiServer } from "./apiServer";
|
||||||
import { createNextServer } from "./nextServer";
|
import { createNextServer } from "./nextServer";
|
||||||
import { createInternalServer } from "./internalServer";
|
import { createInternalServer } from "./internalServer";
|
||||||
import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "./db/schemas";
|
import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "@server/db";
|
||||||
import { createIntegrationApiServer } from "./integrationApiServer";
|
import { createIntegrationApiServer } from "./integrationApiServer";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { roleResources, userResources } from "@server/db/schemas";
|
import { roleResources, userResources } from "@server/db";
|
||||||
|
|
||||||
export async function canUserAccessResource({
|
export async function canUserAccessResource({
|
||||||
userId,
|
userId,
|
||||||
|
|
|
@ -1,219 +1,10 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { __DIRNAME, APP_VERSION } from "@server/lib/consts";
|
import { __DIRNAME, APP_VERSION } from "@server/lib/consts";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { SupporterKey, supporterKey } from "@server/db/schemas";
|
import { SupporterKey, supporterKey } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { license } from "@server/license/license";
|
import { license } from "@server/license/license";
|
||||||
import { readConfigFile } from "./readConfigFile";
|
import { configSchema, readConfigFile } from "./readConfigFile";
|
||||||
import stoi from "./stoi";
|
|
||||||
import { passwordSchema } from "@server/auth/passwordSchema";
|
|
||||||
|
|
||||||
const portSchema = z.number().positive().gt(0).lte(65535);
|
|
||||||
|
|
||||||
const getEnvOrYaml = (envVar: string) => (valFromYaml: any) => {
|
|
||||||
return process.env[envVar] ?? valFromYaml;
|
|
||||||
};
|
|
||||||
|
|
||||||
const configSchema = z.object({
|
|
||||||
app: z.object({
|
|
||||||
dashboard_url: z
|
|
||||||
.string()
|
|
||||||
.url()
|
|
||||||
.optional()
|
|
||||||
.pipe(z.string().url())
|
|
||||||
.transform((url) => url.toLowerCase()),
|
|
||||||
log_level: z
|
|
||||||
.enum(["debug", "info", "warn", "error"])
|
|
||||||
.optional()
|
|
||||||
.default("info"),
|
|
||||||
save_logs: z.boolean().optional().default(false),
|
|
||||||
log_failed_attempts: z.boolean().optional().default(false)
|
|
||||||
}),
|
|
||||||
domains: z
|
|
||||||
.record(
|
|
||||||
z.string(),
|
|
||||||
z.object({
|
|
||||||
base_domain: z
|
|
||||||
.string()
|
|
||||||
.nonempty("base_domain must not be empty")
|
|
||||||
.transform((url) => url.toLowerCase()),
|
|
||||||
cert_resolver: z.string().optional().default("letsencrypt"),
|
|
||||||
prefer_wildcard_cert: z.boolean().optional().default(false)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.refine(
|
|
||||||
(domains) => {
|
|
||||||
const keys = Object.keys(domains);
|
|
||||||
|
|
||||||
if (keys.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: "At least one domain must be defined"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
server: z.object({
|
|
||||||
integration_port: portSchema
|
|
||||||
.optional()
|
|
||||||
.default(3003)
|
|
||||||
.transform(stoi)
|
|
||||||
.pipe(portSchema.optional()),
|
|
||||||
external_port: portSchema
|
|
||||||
.optional()
|
|
||||||
.default(3000)
|
|
||||||
.transform(stoi)
|
|
||||||
.pipe(portSchema),
|
|
||||||
internal_port: portSchema
|
|
||||||
.optional()
|
|
||||||
.default(3001)
|
|
||||||
.transform(stoi)
|
|
||||||
.pipe(portSchema),
|
|
||||||
next_port: portSchema
|
|
||||||
.optional()
|
|
||||||
.default(3002)
|
|
||||||
.transform(stoi)
|
|
||||||
.pipe(portSchema),
|
|
||||||
internal_hostname: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.default("pangolin")
|
|
||||||
.transform((url) => url.toLowerCase()),
|
|
||||||
session_cookie_name: z.string().optional().default("p_session_token"),
|
|
||||||
resource_access_token_param: z.string().optional().default("p_token"),
|
|
||||||
resource_access_token_headers: z
|
|
||||||
.object({
|
|
||||||
id: z.string().optional().default("P-Access-Token-Id"),
|
|
||||||
token: z.string().optional().default("P-Access-Token")
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
|
||||||
resource_session_request_param: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.default("resource_session_request_param"),
|
|
||||||
dashboard_session_length_hours: z
|
|
||||||
.number()
|
|
||||||
.positive()
|
|
||||||
.gt(0)
|
|
||||||
.optional()
|
|
||||||
.default(720),
|
|
||||||
resource_session_length_hours: z
|
|
||||||
.number()
|
|
||||||
.positive()
|
|
||||||
.gt(0)
|
|
||||||
.optional()
|
|
||||||
.default(720),
|
|
||||||
cors: z
|
|
||||||
.object({
|
|
||||||
origins: z.array(z.string()).optional(),
|
|
||||||
methods: z.array(z.string()).optional(),
|
|
||||||
allowed_headers: z.array(z.string()).optional(),
|
|
||||||
credentials: z.boolean().optional()
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
trust_proxy: z.boolean().optional().default(true),
|
|
||||||
secret: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.transform(getEnvOrYaml("SERVER_SECRET"))
|
|
||||||
.pipe(z.string().min(8))
|
|
||||||
}),
|
|
||||||
traefik: z
|
|
||||||
.object({
|
|
||||||
http_entrypoint: z.string().optional().default("web"),
|
|
||||||
https_entrypoint: z.string().optional().default("websecure"),
|
|
||||||
additional_middlewares: z.array(z.string()).optional()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
|
||||||
gerbil: z
|
|
||||||
.object({
|
|
||||||
start_port: portSchema
|
|
||||||
.optional()
|
|
||||||
.default(51820)
|
|
||||||
.transform(stoi)
|
|
||||||
.pipe(portSchema),
|
|
||||||
base_endpoint: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.pipe(z.string())
|
|
||||||
.transform((url) => url.toLowerCase()),
|
|
||||||
use_subdomain: z.boolean().optional().default(false),
|
|
||||||
subnet_group: z.string().optional().default("100.89.137.0/20"),
|
|
||||||
block_size: z.number().positive().gt(0).optional().default(24),
|
|
||||||
site_block_size: z.number().positive().gt(0).optional().default(30)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
|
||||||
rate_limits: z
|
|
||||||
.object({
|
|
||||||
global: z
|
|
||||||
.object({
|
|
||||||
window_minutes: z
|
|
||||||
.number()
|
|
||||||
.positive()
|
|
||||||
.gt(0)
|
|
||||||
.optional()
|
|
||||||
.default(1),
|
|
||||||
max_requests: z
|
|
||||||
.number()
|
|
||||||
.positive()
|
|
||||||
.gt(0)
|
|
||||||
.optional()
|
|
||||||
.default(500)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
|
||||||
auth: z
|
|
||||||
.object({
|
|
||||||
window_minutes: z.number().positive().gt(0),
|
|
||||||
max_requests: z.number().positive().gt(0)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
|
||||||
email: z
|
|
||||||
.object({
|
|
||||||
smtp_host: z.string().optional(),
|
|
||||||
smtp_port: portSchema.optional(),
|
|
||||||
smtp_user: z.string().optional(),
|
|
||||||
smtp_pass: z.string().optional(),
|
|
||||||
smtp_secure: z.boolean().optional(),
|
|
||||||
smtp_tls_reject_unauthorized: z.boolean().optional(),
|
|
||||||
no_reply: z.string().email().optional()
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
users: z.object({
|
|
||||||
server_admin: z.object({
|
|
||||||
email: z
|
|
||||||
.string()
|
|
||||||
.email()
|
|
||||||
.optional()
|
|
||||||
.transform(getEnvOrYaml("USERS_SERVERADMIN_EMAIL"))
|
|
||||||
.pipe(z.string().email())
|
|
||||||
.transform((v) => v.toLowerCase()),
|
|
||||||
password: passwordSchema
|
|
||||||
.optional()
|
|
||||||
.transform(getEnvOrYaml("USERS_SERVERADMIN_PASSWORD"))
|
|
||||||
.pipe(passwordSchema)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
flags: z
|
|
||||||
.object({
|
|
||||||
require_email_verification: z.boolean().optional(),
|
|
||||||
disable_signup_without_invite: z.boolean().optional(),
|
|
||||||
disable_user_create_org: z.boolean().optional(),
|
|
||||||
allow_raw_resources: z.boolean().optional(),
|
|
||||||
allow_base_domain_resources: z.boolean().optional(),
|
|
||||||
allow_local_sites: z.boolean().optional(),
|
|
||||||
enable_integration_api: z.boolean().optional()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private rawConfig!: z.infer<typeof configSchema>;
|
private rawConfig!: z.infer<typeof configSchema>;
|
||||||
|
|
|
@ -119,28 +119,10 @@ export const configSchema = z.object({
|
||||||
.transform(getEnvOrYaml("SERVER_SECRET"))
|
.transform(getEnvOrYaml("SERVER_SECRET"))
|
||||||
.pipe(z.string().min(8))
|
.pipe(z.string().min(8))
|
||||||
}),
|
}),
|
||||||
database: z
|
postgres: z
|
||||||
.object({
|
.object({
|
||||||
type: z.enum(["sqlite", "postgres"]).optional().default("sqlite"),
|
connection_string: z.string().optional()
|
||||||
postgres: z
|
|
||||||
.object({
|
|
||||||
connection_string: z.string()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
})
|
})
|
||||||
.refine(
|
|
||||||
(data) => {
|
|
||||||
if (data.type === "postgres" && !data.postgres) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message:
|
|
||||||
"Postgres config required"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.default({}),
|
.default({}),
|
||||||
traefik: z
|
traefik: z
|
||||||
.object({
|
.object({
|
||||||
|
@ -230,7 +212,8 @@ export const configSchema = z.object({
|
||||||
disable_user_create_org: z.boolean().optional(),
|
disable_user_create_org: z.boolean().optional(),
|
||||||
allow_raw_resources: z.boolean().optional(),
|
allow_raw_resources: z.boolean().optional(),
|
||||||
allow_base_domain_resources: z.boolean().optional(),
|
allow_base_domain_resources: z.boolean().optional(),
|
||||||
allow_local_sites: z.boolean().optional()
|
allow_local_sites: z.boolean().optional(),
|
||||||
|
enable_integration_api: z.boolean().optional()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { hostMeta, licenseKey, sites } from "@server/db/schemas";
|
import { hostMeta, licenseKey, sites } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
import { validateJWT } from "./licenseJwt";
|
import { validateJWT } from "./licenseJwt";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs, orgs } from "@server/db/schemas";
|
import { userOrgs, orgs } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceAccessToken, resources, apiKeyOrg } from "@server/db/schemas";
|
import { resourceAccessToken, resources, apiKeyOrg } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { verifyPassword } from "@server/auth/password";
|
import { verifyPassword } from "@server/auth/password";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeys } from "@server/db/schemas";
|
import { apiKeys } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeys, apiKeyOrg } from "@server/db/schemas";
|
import { apiKeys, apiKeyOrg } from "@server/db";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -3,8 +3,8 @@ import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { ActionsEnum } from "@server/auth/actions";
|
import { ActionsEnum } from "@server/auth/actions";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeyActions } from "@server/db/schemas";
|
import { apiKeyActions } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
|
||||||
export function verifyApiKeyHasAction(action: ActionsEnum) {
|
export function verifyApiKeyHasAction(action: ActionsEnum) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeyOrg } from "@server/db/schemas";
|
import { apiKeyOrg } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources, apiKeyOrg } from "@server/db/schemas";
|
import { resources, apiKeyOrg } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roles, apiKeyOrg } from "@server/db/schemas";
|
import { roles, apiKeyOrg } from "@server/db";
|
||||||
import { and, eq, inArray } from "drizzle-orm";
|
import { and, eq, inArray } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq, inArray } from "drizzle-orm";
|
import { and, eq, inArray } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { db } from "@server/db";
|
||||||
import {
|
import {
|
||||||
sites,
|
sites,
|
||||||
apiKeyOrg
|
apiKeyOrg
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources, targets, apiKeyOrg } from "@server/db/schemas";
|
import { resources, targets, apiKeyOrg } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceAccessToken, resources, userOrgs } from "@server/db/schemas";
|
import { resourceAccessToken, resources, userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roles, userOrgs } from "@server/db/schemas";
|
import { roles, userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs, apiKeys, apiKeyOrg } from "@server/db/schemas";
|
import { userOrgs, apiKeys, apiKeyOrg } from "@server/db";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
userOrgs,
|
userOrgs,
|
||||||
userResources,
|
userResources,
|
||||||
roleResources,
|
roleResources,
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roles, userOrgs } from "@server/db/schemas";
|
import { roles, userOrgs } from "@server/db";
|
||||||
import { and, eq, inArray } from "drizzle-orm";
|
import { and, eq, inArray } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { NextFunction, Response } from "express";
|
import { NextFunction, Response } from "express";
|
||||||
import ErrorResponse from "@server/types/ErrorResponse";
|
import ErrorResponse from "@server/types/ErrorResponse";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users } from "@server/db/schemas";
|
import { users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq, inArray, or } from "drizzle-orm";
|
import { and, eq, inArray, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
userSites,
|
userSites,
|
||||||
roleSites,
|
roleSites,
|
||||||
roles,
|
roles,
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources, targets, userOrgs } from "@server/db/schemas";
|
import { resources, targets, userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { NextFunction, Response } from "express";
|
import { NextFunction, Response } from "express";
|
||||||
import ErrorResponse from "@server/types/ErrorResponse";
|
import ErrorResponse from "@server/types/ErrorResponse";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users } from "@server/db/schemas";
|
import { users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schemas";
|
import { userOrgs } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -5,9 +5,9 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { resourceAccessToken } from "@server/db/schemas";
|
import { resourceAccessToken } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const deleteAccessTokenParamsSchema = z
|
const deleteAccessTokenParamsSchema = z
|
||||||
|
|
|
@ -4,12 +4,12 @@ import {
|
||||||
generateIdFromEntropySize,
|
generateIdFromEntropySize,
|
||||||
SESSION_COOKIE_EXPIRES
|
SESSION_COOKIE_EXPIRES
|
||||||
} from "@server/auth/sessions/app";
|
} from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import {
|
import {
|
||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
resources
|
resources
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
roleResources,
|
roleResources,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
sites
|
sites
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
|
import { apiKeyOrg, apiKeys } from "@server/db";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { apiKeyOrg, apiKeys, orgs } from "@server/db/schemas";
|
import { apiKeyOrg, apiKeys, orgs } from "@server/db";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeys } from "@server/db/schemas";
|
import { apiKeys } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
|
import { apiKeyOrg, apiKeys } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeys } from "@server/db/schemas";
|
import { apiKeys } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { actions, apiKeyActions, apiKeyOrg, apiKeys } from "@server/db/schemas";
|
import { actions, apiKeyActions, apiKeyOrg, apiKeys } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
|
import { apiKeyOrg, apiKeys } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeys } from "@server/db/schemas";
|
import { apiKeys } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { actions, apiKeyActions } from "@server/db/schemas";
|
import { actions, apiKeyActions } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { apiKeyOrg, orgs } from "@server/db/schemas";
|
import { apiKeyOrg, orgs } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schemas";
|
import { User, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schemas";
|
import { User, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { verifyPassword } from "@server/auth/password";
|
import { verifyPassword } from "@server/auth/password";
|
||||||
|
|
|
@ -3,8 +3,8 @@ import {
|
||||||
generateSessionToken,
|
generateSessionToken,
|
||||||
serializeSessionCookie
|
serializeSessionCookie
|
||||||
} from "@server/auth/sessions/app";
|
} from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users } from "@server/db/schemas";
|
import { users } from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { User } from "@server/db/schemas";
|
import { User } from "@server/db";
|
||||||
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
|
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { passwordResetTokens, users } from "@server/db/schemas";
|
import { passwordResetTokens, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
||||||
import { createDate } from "oslo";
|
import { createDate } from "oslo";
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { encodeHex } from "oslo/encoding";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schemas";
|
import { User, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { createTOTPKeyURI } from "oslo/otp";
|
import { createTOTPKeyURI } from "oslo/otp";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { fromError } from "zod-validation-error";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { passwordResetTokens, users } from "@server/db/schemas";
|
import { passwordResetTokens, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { hashPassword, verifyPassword } from "@server/auth/password";
|
import { hashPassword, verifyPassword } from "@server/auth/password";
|
||||||
import { verifyTotpCode } from "@server/auth/totp";
|
import { verifyTotpCode } from "@server/auth/totp";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { users } from "@server/db/schemas";
|
import { users } from "@server/db";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, emailVerificationCodes, users } from "@server/db/schemas";
|
import { User, emailVerificationCodes, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { twoFactorBackupCodes, User, users } from "@server/db/schemas";
|
import { twoFactorBackupCodes, User, users } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { alphabet, generateRandomString } from "oslo/crypto";
|
import { alphabet, generateRandomString } from "oslo/crypto";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
|
|
@ -4,8 +4,8 @@ import createHttpError from "http-errors";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { resourceAccessToken, resources, sessions } from "@server/db/schemas";
|
import { resourceAccessToken, resources, sessions } from "@server/db";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
createResourceSession,
|
createResourceSession,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
validateResourceSessionToken
|
validateResourceSessionToken
|
||||||
} from "@server/auth/sessions/resource";
|
} from "@server/auth/sessions/resource";
|
||||||
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import {
|
import {
|
||||||
Resource,
|
Resource,
|
||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
|
@ -21,7 +21,7 @@ import {
|
||||||
userOrgs,
|
userOrgs,
|
||||||
userResources,
|
userResources,
|
||||||
users
|
users
|
||||||
} from "@server/db/schemas";
|
} from "@server/db";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { isIpInCidr } from "@server/lib/ip";
|
import { isIpInCidr } from "@server/lib/ip";
|
||||||
import { response } from "@server/lib/response";
|
import { response } from "@server/lib/response";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { domains, orgDomains, users } from "@server/db/schemas";
|
import { domains, orgDomains, users } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { sites, resources, targets, exitNodes } from '@server/db/schemas';
|
import { sites, resources, targets, exitNodes } from "@server/db";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { getUniqueExitNodeEndpointName } from '@server/db/names';
|
import { getUniqueExitNodeEndpointName } from "../../db/names";
|
||||||
import { findNextAvailableCidr } from "@server/lib/ip";
|
import { findNextAvailableCidr } from "@server/lib/ip";
|
||||||
import { fromError } from 'zod-validation-error';
|
import { fromError } from "zod-validation-error";
|
||||||
import { getAllowedIps } from '../target/helpers';
|
import { getAllowedIps } from "../target/helpers";
|
||||||
// Define Zod schema for request validation
|
// Define Zod schema for request validation
|
||||||
const getConfigSchema = z.object({
|
const getConfigSchema = z.object({
|
||||||
publicKey: z.string(),
|
publicKey: z.string(),
|
||||||
reachableAt: z.string().optional(),
|
reachableAt: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GetConfigResponse = {
|
export type GetConfigResponse = {
|
||||||
|
@ -25,9 +25,13 @@ export type GetConfigResponse = {
|
||||||
publicKey: string | null;
|
publicKey: string | null;
|
||||||
allowedIps: string[];
|
allowedIps: string[];
|
||||||
}[];
|
}[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function getConfig(req: Request, res: Response, next: NextFunction): Promise<any> {
|
export async function getConfig(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = getConfigSchema.safeParse(req.body);
|
const parsedParams = getConfigSchema.safeParse(req.body);
|
||||||
|
@ -43,11 +47,16 @@ export async function getConfig(req: Request, res: Response, next: NextFunction)
|
||||||
const { publicKey, reachableAt } = parsedParams.data;
|
const { publicKey, reachableAt } = parsedParams.data;
|
||||||
|
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'publicKey is required'));
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, "publicKey is required")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch exit node
|
// Fetch exit node
|
||||||
let exitNodeQuery = await db.select().from(exitNodes).where(eq(exitNodes.publicKey, publicKey));
|
let exitNodeQuery = await db
|
||||||
|
.select()
|
||||||
|
.from(exitNodes)
|
||||||
|
.where(eq(exitNodes.publicKey, publicKey));
|
||||||
let exitNode;
|
let exitNode;
|
||||||
if (exitNodeQuery.length === 0) {
|
if (exitNodeQuery.length === 0) {
|
||||||
const address = await getNextAvailableSubnet();
|
const address = await getNextAvailableSubnet();
|
||||||
|
@ -60,40 +69,53 @@ export async function getConfig(req: Request, res: Response, next: NextFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new exit node
|
// create a new exit node
|
||||||
exitNode = await db.insert(exitNodes).values({
|
exitNode = await db
|
||||||
publicKey,
|
.insert(exitNodes)
|
||||||
endpoint: `${subEndpoint}${subEndpoint != "" ? "." : ""}${config.getRawConfig().gerbil.base_endpoint}`,
|
.values({
|
||||||
address,
|
publicKey,
|
||||||
listenPort,
|
endpoint: `${subEndpoint}${subEndpoint != "" ? "." : ""}${config.getRawConfig().gerbil.base_endpoint}`,
|
||||||
reachableAt,
|
address,
|
||||||
name: `Exit Node ${publicKey.slice(0, 8)}`,
|
listenPort,
|
||||||
}).returning().execute();
|
reachableAt,
|
||||||
|
name: `Exit Node ${publicKey.slice(0, 8)}`
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.execute();
|
||||||
|
|
||||||
logger.info(`Created new exit node ${exitNode[0].name} with address ${exitNode[0].address} and port ${exitNode[0].listenPort}`);
|
logger.info(
|
||||||
|
`Created new exit node ${exitNode[0].name} with address ${exitNode[0].address} and port ${exitNode[0].listenPort}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
exitNode = exitNodeQuery;
|
exitNode = exitNodeQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exitNode) {
|
if (!exitNode) {
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to create exit node"));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to create exit node"
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch sites for this exit node
|
const sitesRes = await db
|
||||||
const sitesRes = await db.query.sites.findMany({
|
.select()
|
||||||
where: eq(sites.exitNodeId, exitNode[0].exitNodeId),
|
.from(sites)
|
||||||
});
|
.where(eq(sites.exitNodeId, exitNode[0].exitNodeId));
|
||||||
|
|
||||||
const peers = await Promise.all(sitesRes.map(async (site) => {
|
const peers = await Promise.all(
|
||||||
return {
|
sitesRes.map(async (site) => {
|
||||||
publicKey: site.pubKey,
|
return {
|
||||||
allowedIps: await getAllowedIps(site.siteId)
|
publicKey: site.pubKey,
|
||||||
};
|
allowedIps: await getAllowedIps(site.siteId)
|
||||||
}));
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const configResponse: GetConfigResponse = {
|
const configResponse: GetConfigResponse = {
|
||||||
listenPort: exitNode[0].listenPort || 51820,
|
listenPort: exitNode[0].listenPort || 51820,
|
||||||
ipAddress: exitNode[0].address,
|
ipAddress: exitNode[0].address,
|
||||||
peers,
|
peers
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.debug("Sending config: ", configResponse);
|
logger.debug("Sending config: ", configResponse);
|
||||||
|
@ -101,32 +123,49 @@ export async function getConfig(req: Request, res: Response, next: NextFunction)
|
||||||
return res.status(HttpCode.OK).send(configResponse);
|
return res.status(HttpCode.OK).send(configResponse);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNextAvailableSubnet(): Promise<string> {
|
async function getNextAvailableSubnet(): Promise<string> {
|
||||||
// Get all existing subnets from routes table
|
// Get all existing subnets from routes table
|
||||||
const existingAddresses = await db.select({
|
const existingAddresses = await db
|
||||||
address: exitNodes.address,
|
.select({
|
||||||
}).from(exitNodes);
|
address: exitNodes.address
|
||||||
|
})
|
||||||
|
.from(exitNodes);
|
||||||
|
|
||||||
const addresses = existingAddresses.map(a => a.address);
|
const addresses = existingAddresses.map((a) => a.address);
|
||||||
let subnet = findNextAvailableCidr(addresses, config.getRawConfig().gerbil.block_size, config.getRawConfig().gerbil.subnet_group);
|
let subnet = findNextAvailableCidr(
|
||||||
|
addresses,
|
||||||
|
config.getRawConfig().gerbil.block_size,
|
||||||
|
config.getRawConfig().gerbil.subnet_group
|
||||||
|
);
|
||||||
if (!subnet) {
|
if (!subnet) {
|
||||||
throw new Error('No available subnets remaining in space');
|
throw new Error("No available subnets remaining in space");
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace the last octet with 1
|
// replace the last octet with 1
|
||||||
subnet = subnet.split('.').slice(0, 3).join('.') + '.1' + '/' + subnet.split('/')[1];
|
subnet =
|
||||||
|
subnet.split(".").slice(0, 3).join(".") +
|
||||||
|
".1" +
|
||||||
|
"/" +
|
||||||
|
subnet.split("/")[1];
|
||||||
return subnet;
|
return subnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNextAvailablePort(): Promise<number> {
|
async function getNextAvailablePort(): Promise<number> {
|
||||||
// Get all existing ports from exitNodes table
|
// Get all existing ports from exitNodes table
|
||||||
const existingPorts = await db.select({
|
const existingPorts = await db
|
||||||
listenPort: exitNodes.listenPort,
|
.select({
|
||||||
}).from(exitNodes);
|
listenPort: exitNodes.listenPort
|
||||||
|
})
|
||||||
|
.from(exitNodes);
|
||||||
|
|
||||||
// Find the first available port between 1024 and 65535
|
// Find the first available port between 1024 and 65535
|
||||||
let nextPort = config.getRawConfig().gerbil.start_port;
|
let nextPort = config.getRawConfig().gerbil.start_port;
|
||||||
|
@ -136,7 +175,7 @@ async function getNextAvailablePort(): Promise<number> {
|
||||||
}
|
}
|
||||||
nextPort++;
|
nextPort++;
|
||||||
if (nextPort > 65535) {
|
if (nextPort > 65535) {
|
||||||
throw new Error('No available ports remaining in space');
|
throw new Error("No available ports remaining in space");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
import db from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { exitNodes } from '@server/db/schemas';
|
import { exitNodes } from '@server/db';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
export async function addPeer(exitNodeId: number, peer: {
|
export async function addPeer(exitNodeId: number, peer: {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { DrizzleError, eq } from "drizzle-orm";
|
import { DrizzleError, eq } from "drizzle-orm";
|
||||||
import { sites, resources, targets, exitNodes } from "@server/db/schemas";
|
import { sites, resources, targets, exitNodes } from "@server/db";
|
||||||
import db from "@server/db";
|
import { db } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
@ -29,10 +29,11 @@ export const receiveBandwidth = async (
|
||||||
for (const peer of bandwidthData) {
|
for (const peer of bandwidthData) {
|
||||||
const { publicKey, bytesIn, bytesOut } = peer;
|
const { publicKey, bytesIn, bytesOut } = peer;
|
||||||
|
|
||||||
// Find the site by public key
|
const [site] = await trx
|
||||||
const site = await trx.query.sites.findFirst({
|
.select()
|
||||||
where: eq(sites.pubKey, publicKey)
|
.from(sites)
|
||||||
});
|
.where(eq(sites.pubKey, publicKey))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (!site) {
|
if (!site) {
|
||||||
logger.warn(`Site not found for public key: ${publicKey}`);
|
logger.warn(`Site not found for public key: ${publicKey}`);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { idp, idpOrg } from "@server/db/schemas";
|
import { idp, idpOrg } from "@server/db";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
|
|
|
@ -7,7 +7,7 @@ import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { idp, idpOidcConfig, idpOrg, orgs } from "@server/db/schemas";
|
import { idp, idpOidcConfig, idpOrg, orgs } from "@server/db";
|
||||||
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
||||||
import { encrypt } from "@server/lib/crypto";
|
import { encrypt } from "@server/lib/crypto";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
|
@ -6,7 +6,7 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { idp, idpOidcConfig, idpOrg } from "@server/db/schemas";
|
import { idp, idpOidcConfig, idpOrg } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { idp, idpOrg } from "@server/db/schemas";
|
import { idp, idpOrg } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { idp, idpOidcConfig, idpOrg } from "@server/db/schemas";
|
import { idp, idpOidcConfig, idpOrg } from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import * as arctic from "arctic";
|
import * as arctic from "arctic";
|
||||||
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { idp, idpOidcConfig } from "@server/db/schemas";
|
import { idp, idpOidcConfig } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { idpOrg } from "@server/db/schemas";
|
import { idpOrg } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { domains, idp, orgDomains, users, idpOrg } from "@server/db/schemas";
|
import { domains, idp, orgDomains, users, idpOrg } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|
|
@ -8,7 +8,7 @@ import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { idp, idpOrg } from "@server/db/schemas";
|
import { idp, idpOrg } from "@server/db";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue