Init Commit
quality / commitlint (push) Skipped
CD / update-wiki (push) Failing after 7s
quality / changes (push) Successful in 4s
quality / docker-check (push) Skipped
quality / web (push) Failing after 38s
quality / api (push) Successful in 49s
CD / quality (push) Failing after 1m36s
CD / publish (push) Skipped

This commit is contained in:
Denozordec
2026-09-04 11:48:19 +07:00
commit cb8a79260e
300 changed files with 42404 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
export { }
+417
View File
@@ -0,0 +1,417 @@
// src/server.ts
import { readFileSync, existsSync } from "fs";
import { resolve as resolve3 } from "path";
// src/app.ts
import { resolve as resolve2 } from "path";
import Fastify from "fastify";
import {
serializerCompiler,
validatorCompiler
} from "@fastify/type-provider-zod";
// src/config.ts
import { resolve } from "path";
function boolEnv(v, fallback) {
if (v === void 0 || v === "") return fallback;
return v === "1" || v.toLowerCase() === "true";
}
function loadConfig() {
const isProd = process.env.NODE_ENV === "production";
const jwtSecret = process.env.AUTH_JWT_SECRET ?? process.env.JWT_SECRET ?? (isProd ? "" : "dev-secret-change-me");
return {
databaseUrl: process.env.DATABASE_URL ?? "sqlite:data/app.db",
jwtSecret: jwtSecret || "dev-secret-change-me",
jwtTtlHours: Number(process.env.JWT_TTL_HOURS ?? "24") || 24,
adminUsername: process.env.ADMIN_USERNAME ?? "admin",
adminPasswordHash: process.env.ADMIN_PASSWORD_HASH?.trim() || "devplaceholder",
serverPort: Number(process.env.SERVER_PORT ?? "8081") || 8081,
staticDir: process.env.STATIC_DIR ? resolve(process.env.STATIC_DIR) : null,
logLevel: process.env.LOG_LEVEL ?? "info",
authRequired: boolEnv(process.env.AUTH_REQUIRED, false),
authIssuer: process.env.AUTH_ISSUER ?? process.env.ISSUER ?? "https://auth.shnt.top",
authPortalUrl: (process.env.AUTH_PORTAL_URL ?? process.env.VITE_AUTH_PORTAL_URL ?? "http://localhost:5175").replace(/\/$/, ""),
authAuditIngestSecret: process.env.AUTH_AUDIT_INGEST_SECRET?.trim() || (!isProd ? "dev-audit-ingest-secret" : null)
};
}
// src/plugins/auth.ts
import fp from "fastify-plugin";
// src/errors.ts
import { NotFoundError, ConflictError } from "@cdnmanager/db";
import { ValidationError } from "@cdnmanager/shared";
var AppError = class _AppError extends Error {
constructor(code, message, statusCode) {
super(message);
this.code = code;
this.statusCode = statusCode;
this.name = "AppError";
}
code;
statusCode;
static notFound(message) {
return new _AppError("NOT_FOUND", message, 404);
}
static validation(message) {
return new _AppError("VALIDATION_ERROR", message, 400);
}
static unauthorized() {
return new _AppError("UNAUTHORIZED", "unauthorized", 401);
}
static forbidden(message = "forbidden") {
return new _AppError("FORBIDDEN", message, 403);
}
static conflict(message) {
return new _AppError("CONFLICT", message, 409);
}
static cloudflare(message) {
return new _AppError("CLOUDFLARE_ERROR", message, 502);
}
static dnsUpdateFailed(message) {
return new _AppError(
"DNS_UPDATE_FAILED",
message,
502
);
}
static healthcheckCreateFailed(message) {
return new _AppError("HEALTHCHECK_CREATE_FAILED", message, 502);
}
static zoneNotFound(message = "\u0437\u043E\u043D\u0430 Cloudflare \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430") {
return new _AppError("ZONE_NOT_FOUND", message, 404);
}
static invalidIp(message = "\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 IP-\u0430\u0434\u0440\u0435\u0441") {
return new _AppError("INVALID_IP", message, 400);
}
static invalidHostname(message = "\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0438\u043C\u044F \u0445\u043E\u0441\u0442\u0430") {
return new _AppError("INVALID_HOSTNAME", message, 400);
}
static rateLimited(message = "Cloudflare \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u043B \u0437\u0430\u043F\u0440\u043E\u0441\u044B. \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u043E\u043F\u044B\u0442\u043A\u0443.") {
return new _AppError("RATE_LIMITED", message, 429);
}
static cloudflareAuthFailed(message = "Cloudflare \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430") {
return new _AppError("CLOUDFLARE_AUTH_FAILED", message, 401);
}
static syncFailed(message) {
return new _AppError("SYNC_FAILED", message, 502);
}
static internal(message) {
return new _AppError("INTERNAL_ERROR", message, 500);
}
};
function toAppError(err) {
if (err instanceof AppError) return err;
if (err instanceof NotFoundError) return AppError.notFound(err.message);
if (err instanceof ConflictError) return AppError.conflict(err.message);
if (err instanceof ValidationError) return AppError.validation(err.message);
if (err instanceof Error) return AppError.internal(err.message);
return AppError.internal(String(err));
}
function errorBody(err) {
return {
error: {
code: err.code,
message: err.message
}
};
}
// src/lib/permissions.ts
function hasPermission(granted, required) {
if (granted.includes(required)) return true;
const parts = required.split(":");
if (parts.length !== 3) return false;
const [app2, section, action] = parts;
if (action === "read") {
return granted.includes(`${app2}:${section}:write`) || granted.includes(`${app2}:${section}:admin`);
}
if (action === "write") {
return granted.includes(`${app2}:${section}:admin`);
}
return false;
}
var RULES = [
{
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
match: (p) => p.startsWith("/api/v1/settings"),
permission: "cdn:settings:admin"
}
];
function permissionForRequest(method, path) {
const m = method.toUpperCase();
const pathname = path.split("?")[0] ?? path;
for (const rule of RULES) {
if (!rule.methods.includes(m)) continue;
if (rule.match(pathname)) return rule.permission;
}
if (pathname.startsWith("/api/v1/")) return "cdn:dashboard:read";
return null;
}
// src/plugins/auth.ts
async function authPlugin(app2, opts) {
const { config: config2 } = opts;
if (config2.authRequired && (!config2.jwtSecret || config2.jwtSecret.length < 8)) {
throw new Error(
"AUTH_JWT_SECRET / JWT_SECRET required when AUTH_REQUIRED=true"
);
}
await app2.register(import("@fastify/jwt"), {
secret: config2.jwtSecret,
...config2.authRequired ? {
verify: {
allowedIss: [config2.authIssuer]
}
} : {}
});
app2.decorate("config", config2);
if (config2.authRequired) {
app2.log.info(
{ issuer: config2.authIssuer, portal: config2.authPortalUrl },
"AUTH_REQUIRED=true \u2014 portal JWT middleware enabled"
);
} else {
app2.log.info("AUTH_REQUIRED=false \u2014 local JWT / open protected routes with requireAuth");
}
}
async function requireAuth(request, reply) {
const config2 = request.server.config;
const authHeader = request.headers.authorization ?? "";
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
if (!token) throw AppError.unauthorized();
try {
await request.jwtVerify();
} catch {
throw AppError.unauthorized();
}
if (!config2.authRequired) {
return;
}
const payload = request.user;
const apps = Array.isArray(payload.apps) ? payload.apps.map(String) : [];
const permissions = Array.isArray(payload.permissions) ? payload.permissions.map(String) : [];
if (!apps.includes("cdn")) {
throw AppError.forbidden("\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044E CDN Manager");
}
request.authUser = {
id: String(payload.sub),
email: String(payload.email ?? ""),
name: String(payload.name ?? ""),
apps,
permissions,
isAdmin: Boolean(payload.is_admin)
};
const required = permissionForRequest(request.method, request.url);
if (required && !hasPermission(permissions, required)) {
throw AppError.forbidden(`\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043F\u0440\u0430\u0432: ${required}`);
}
}
var auth_default = fp(authPlugin, { name: "auth" });
// src/plugins/cors.ts
import fp2 from "fastify-plugin";
async function corsPlugin(app2) {
await app2.register(import("@fastify/cors"), {
origin: true,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"]
});
}
var cors_default = fp2(corsPlugin, { name: "cors" });
// src/plugins/db.ts
import fp3 from "fastify-plugin";
import {
createDb,
createMemoryDb,
healthCheck,
runMigrations
} from "@cdnmanager/db";
async function dbPlugin(app2, opts) {
const { db, sqlite } = opts.memory ? createMemoryDb() : createDb(opts.config.databaseUrl);
runMigrations(sqlite);
app2.decorate("db", db);
app2.decorate("sqlite", sqlite);
app2.addHook("onClose", async () => {
sqlite.close();
});
}
var db_default = fp3(dbPlugin, { name: "db" });
// src/plugins/error-handler.ts
import fp4 from "fastify-plugin";
async function errorHandlerPlugin(app2) {
app2.setErrorHandler((err, _request, reply) => {
if (reply.sent) return;
const appErr = err.statusCode === 401 ? AppError.unauthorized() : toAppError(err);
reply.status(appErr.statusCode).send(errorBody(appErr));
});
}
var error_handler_default = fp4(errorHandlerPlugin, { name: "error-handler" });
// src/routes/health.ts
import { z } from "zod";
// src/services/auth.ts
import { verify } from "@node-rs/argon2";
async function verifyPassword(config2, password) {
if (config2.adminPasswordHash === "devplaceholder") {
if (password === "admin") return;
throw AppError.unauthorized();
}
const ok = await verify(config2.adminPasswordHash, password);
if (!ok) throw AppError.unauthorized();
}
async function login(config2, sign, req) {
if (req.username !== config2.adminUsername) {
throw AppError.unauthorized();
}
await verifyPassword(config2, req.password);
const expiresAt = new Date(
Date.now() + config2.jwtTtlHours * 60 * 60 * 1e3
);
const token = sign({
sub: req.username,
exp: Math.floor(expiresAt.getTime() / 1e3)
});
return {
token,
expires_at: expiresAt.toISOString()
};
}
// src/routes/health.ts
async function healthRoutes(app2) {
app2.get("/health", async (request) => {
healthCheck(request.server.sqlite);
return { status: "ok" };
});
app2.get("/ready", async (request) => {
healthCheck(request.server.sqlite);
return {
status: "ready",
database: true
};
});
}
async function authRoutes(app2) {
app2.get("/auth/config", async (request) => {
const { config: config2 } = request.server;
return {
required: config2.authRequired,
portal_url: config2.authPortalUrl
};
});
const loginSchema = z.object({
username: z.string(),
password: z.string()
});
app2.post("/auth/login", async (request, reply) => {
if (request.server.config.authRequired) {
return reply.code(403).send({
error: {
code: "FORBIDDEN",
message: "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0432\u0445\u043E\u0434 \u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 auth-portal"
}
});
}
const body = loginSchema.parse(request.body);
const result = await login(
request.server.config,
(payload) => request.server.jwt.sign(payload),
body
);
return result;
});
}
// src/routes/settings.ts
import { appSettingsPatchSchema } from "@cdnmanager/shared";
import { getAppSettings, updateAppSettings } from "@cdnmanager/db";
async function settingsRoutes(app2) {
app2.get("/settings", async (request) => {
return getAppSettings(request.server.db);
});
app2.patch("/settings", async (request) => {
const parsed = appSettingsPatchSchema.safeParse(request.body);
if (!parsed.success) {
throw AppError.validation(
parsed.error.issues[0]?.message ?? "\u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438"
);
}
return updateAppSettings(request.server.db, parsed.data);
});
}
// src/app.ts
async function buildApp(opts = {}) {
const config2 = opts.config ?? loadConfig();
const app2 = Fastify({
logger: { level: config2.logLevel }
}).withTypeProvider();
app2.setValidatorCompiler(validatorCompiler);
app2.setSerializerCompiler(serializerCompiler);
await app2.register(import("@fastify/sensible"));
await app2.register(import("@fastify/helmet"), { contentSecurityPolicy: false });
await app2.register(import("@fastify/rate-limit"), {
max: 300,
timeWindow: "1 minute"
});
await app2.register(cors_default);
await app2.register(error_handler_default);
await app2.register(db_default, { config: config2, memory: opts.memory });
await app2.register(auth_default, { config: config2 });
await app2.register(healthRoutes);
await app2.register(authRoutes, { prefix: "/api/v1" });
await app2.register(
async (protectedApi) => {
protectedApi.addHook("onRequest", requireAuth);
await protectedApi.register(settingsRoutes);
},
{ prefix: "/api/v1" }
);
const staticDir = config2.staticDir ?? resolve2(process.cwd(), "static");
if (config2.staticDir !== null) {
await app2.register(import("@fastify/static"), {
root: staticDir,
wildcard: false
});
app2.setNotFoundHandler(async (_request, reply) => {
return reply.sendFile("index.html");
});
}
return app2;
}
// src/server.ts
for (const path of [
resolve3(import.meta.dirname, "../../../.env"),
".env",
"../.env"
]) {
if (!existsSync(path)) continue;
const content = readFileSync(path, "utf-8");
for (const line of content.split("\n")) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const eq = trimmed.indexOf("=");
if (eq === -1) continue;
const key = trimmed.slice(0, eq).trim();
let value = trimmed.slice(eq + 1).trim();
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
value = value.slice(1, -1);
}
if (!(key in process.env)) process.env[key] = value;
}
break;
}
var config = loadConfig();
var app = await buildApp({ config });
try {
await app.listen({ port: config.serverPort, host: "0.0.0.0" });
app.log.info(`listening on ${config.serverPort}`);
} catch (err) {
app.log.error(err);
process.exit(1);
}