feat: initial scaffold - backend, frontend, Prisma schema, Docker
- Prisma schema: User, Session, RepoConfig, Preview, Job, WebhookToken, NoConfigComment, AdminSettings - Backend: auth (login/logout/me/first-user setup), webhook handler with HMAC verification, EC2 service, SSH service, deploy pipeline, job queue worker, cron workers - Frontend: Login with first-user detection, Dashboard, PreviewDetail with live log streaming, Settings, Repos config, Admin panel, SetupWizard, Privacy page - Docker Compose and Dockerfile for self-hosted deployment - Uses bcryptjs for Node 24 compatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vendored
+192
@@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var admin_exports = {};
|
||||
__export(admin_exports, {
|
||||
adminListPreviews: () => adminListPreviews,
|
||||
adminStopPreview: () => adminStopPreview,
|
||||
createUser: () => createUser,
|
||||
deleteUser: () => deleteUser,
|
||||
getSettings: () => getSettings,
|
||||
listUsers: () => listUsers,
|
||||
updateSettings: () => updateSettings,
|
||||
updateUser: () => updateUser
|
||||
});
|
||||
module.exports = __toCommonJS(admin_exports);
|
||||
var import_bcryptjs = __toESM(require("bcryptjs"));
|
||||
var import_db = require("../../lib/db");
|
||||
var import_response = require("../../lib/response");
|
||||
var import_errors = require("../../lib/errors");
|
||||
var import_adminSettings = require("../../lib/adminSettings");
|
||||
function requireAdmin(ctr) {
|
||||
const auth = ctr.getAuth?.();
|
||||
if (!auth?.success) return null;
|
||||
if (!auth.user.isAdmin) return null;
|
||||
return auth.user;
|
||||
}
|
||||
async function listUsers(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const users = await import_db.prisma.user.findMany({
|
||||
select: { id: true, username: true, isAdmin: true, isFounder: true, createdAt: true, giteaInstanceUrl: true },
|
||||
orderBy: { createdAt: "asc" }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: users } });
|
||||
}
|
||||
async function createUser(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const body = await ctr.body();
|
||||
const { username, password } = body || {};
|
||||
if (!username || !password) return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Username and password required" } });
|
||||
const existing = await import_db.prisma.user.findUnique({ where: { username } });
|
||||
if (existing) return (0, import_response.makeResponse)({ ctr, content: { code: 409, message: "Username already taken" } });
|
||||
const userCount = await import_db.prisma.user.count();
|
||||
const isFirst = userCount === 0;
|
||||
const hash = await import_bcryptjs.default.hash(password, 12);
|
||||
const user = await import_db.prisma.user.create({
|
||||
data: { username, passwordHash: hash, isAdmin: isFirst, isFounder: isFirst },
|
||||
select: { id: true, username: true, isAdmin: true, isFounder: true }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 201, data: user } });
|
||||
}
|
||||
async function updateUser(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const target = await import_db.prisma.user.findUnique({ where: { id } });
|
||||
if (!target) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: import_errors.ERROR_MESSAGES.NOT_FOUND.message } });
|
||||
const body = await ctr.body();
|
||||
const { username, password, isAdmin } = body || {};
|
||||
const data = {};
|
||||
if (username) {
|
||||
const existing = await import_db.prisma.user.findFirst({ where: { username, id: { not: id } } });
|
||||
if (existing) return (0, import_response.makeResponse)({ ctr, content: { code: 409, message: "Username taken" } });
|
||||
data.username = username;
|
||||
}
|
||||
if (password) data.passwordHash = await import_bcryptjs.default.hash(password, 12);
|
||||
if (isAdmin !== void 0 && !target.isFounder) data.isAdmin = Boolean(isAdmin);
|
||||
await import_db.prisma.user.update({ where: { id }, data });
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "User updated" } });
|
||||
}
|
||||
async function deleteUser(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const target = await import_db.prisma.user.findUnique({ where: { id } });
|
||||
if (!target) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: import_errors.ERROR_MESSAGES.NOT_FOUND.message } });
|
||||
if (target.isFounder) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: "Cannot delete founder" } });
|
||||
if (id === admin.id) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: "Cannot delete yourself" } });
|
||||
await import_db.prisma.user.delete({ where: { id } });
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "User deleted" } });
|
||||
}
|
||||
async function getSettings(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const settings = await (0, import_adminSettings.getAdminSettings)();
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: settings } });
|
||||
}
|
||||
async function updateSettings(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const body = await ctr.body();
|
||||
const {
|
||||
defaultInstanceType,
|
||||
maxConcurrentInstancesPerUser,
|
||||
logSizeLimitBytes,
|
||||
previewRetentionDays,
|
||||
webhookRateLimitPerMinute,
|
||||
contactEmail
|
||||
} = body || {};
|
||||
const data = {};
|
||||
if (defaultInstanceType) data.defaultInstanceType = defaultInstanceType;
|
||||
if (maxConcurrentInstancesPerUser) data.maxConcurrentInstancesPerUser = Number(maxConcurrentInstancesPerUser);
|
||||
if (logSizeLimitBytes) data.logSizeLimitBytes = Number(logSizeLimitBytes);
|
||||
if (previewRetentionDays) data.previewRetentionDays = Number(previewRetentionDays);
|
||||
if (webhookRateLimitPerMinute) data.webhookRateLimitPerMinute = Number(webhookRateLimitPerMinute);
|
||||
if (contactEmail !== void 0) data.contactEmail = contactEmail;
|
||||
await import_db.prisma.adminSettings.upsert({
|
||||
where: { id: 1 },
|
||||
update: data,
|
||||
create: { id: 1, ...data }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Settings updated" } });
|
||||
}
|
||||
async function adminListPreviews(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const previews = await import_db.prisma.preview.findMany({
|
||||
include: {
|
||||
repoConfig: { include: { user: { select: { id: true, username: true } } } }
|
||||
},
|
||||
orderBy: { updatedAt: "desc" },
|
||||
take: 200
|
||||
});
|
||||
return (0, import_response.makeResponse)({
|
||||
ctr,
|
||||
content: {
|
||||
code: 200,
|
||||
data: previews.map((p) => ({
|
||||
id: p.id,
|
||||
prNumber: p.prNumber,
|
||||
prTitle: p.prTitle,
|
||||
status: p.status,
|
||||
instanceIp: p.instanceIp,
|
||||
port: p.port,
|
||||
createdAt: p.createdAt,
|
||||
updatedAt: p.updatedAt,
|
||||
repoOwner: p.repoConfig.repoOwner,
|
||||
repoName: p.repoConfig.repoName,
|
||||
user: p.repoConfig.user
|
||||
}))
|
||||
}
|
||||
});
|
||||
}
|
||||
async function adminStopPreview(ctr) {
|
||||
const admin = requireAdmin(ctr);
|
||||
if (!admin) return (0, import_response.makeResponse)({ ctr, content: { code: 403, message: import_errors.ERROR_MESSAGES.FORBIDDEN.message } });
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const preview = await import_db.prisma.preview.findUnique({ where: { id } });
|
||||
if (!preview) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: import_errors.ERROR_MESSAGES.NOT_FOUND.message } });
|
||||
await import_db.prisma.job.create({
|
||||
data: { previewId: id, type: "STOP", status: "PENDING", payload: { reason: "Admin stop" } }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Stop job enqueued" } });
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
adminListPreviews,
|
||||
adminStopPreview,
|
||||
createUser,
|
||||
deleteUser,
|
||||
getSettings,
|
||||
listUsers,
|
||||
updateSettings,
|
||||
updateUser
|
||||
});
|
||||
//# sourceMappingURL=admin.js.map
|
||||
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var previews_exports = {};
|
||||
__export(previews_exports, {
|
||||
getPreview: () => getPreview,
|
||||
listPreviews: () => listPreviews,
|
||||
previewLogsWs: () => previewLogsWs,
|
||||
stopPreviewRoute: () => stopPreviewRoute
|
||||
});
|
||||
module.exports = __toCommonJS(previews_exports);
|
||||
var import_db = require("../../lib/db");
|
||||
var import_response = require("../../lib/response");
|
||||
var import_errors = require("../../lib/errors");
|
||||
var import_deploy = require("../../services/deploy");
|
||||
function requireAuth(ctr) {
|
||||
const auth = ctr.getAuth?.();
|
||||
if (!auth?.success) return null;
|
||||
return auth.user;
|
||||
}
|
||||
async function listPreviews(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const previews = await import_db.prisma.preview.findMany({
|
||||
where: { repoConfig: { userId: user.id } },
|
||||
include: { repoConfig: { select: { repoOwner: true, repoName: true } } },
|
||||
orderBy: { updatedAt: "desc" }
|
||||
});
|
||||
return (0, import_response.makeResponse)({
|
||||
ctr,
|
||||
content: {
|
||||
code: 200,
|
||||
data: previews.map((p) => ({
|
||||
id: p.id,
|
||||
prNumber: p.prNumber,
|
||||
prTitle: p.prTitle,
|
||||
commitSha: p.commitSha,
|
||||
status: p.status,
|
||||
instanceIp: p.instanceIp,
|
||||
port: p.port,
|
||||
createdAt: p.createdAt,
|
||||
updatedAt: p.updatedAt,
|
||||
lastActivityAt: p.lastActivityAt,
|
||||
repoOwner: p.repoConfig.repoOwner,
|
||||
repoName: p.repoConfig.repoName
|
||||
}))
|
||||
}
|
||||
});
|
||||
}
|
||||
async function getPreview(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const preview = await import_db.prisma.preview.findFirst({
|
||||
where: { id, repoConfig: { userId: user.id } },
|
||||
include: {
|
||||
repoConfig: { select: { repoOwner: true, repoName: true } },
|
||||
jobs: { orderBy: { createdAt: "desc" }, take: 10 }
|
||||
}
|
||||
});
|
||||
if (!preview) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: import_errors.ERROR_MESSAGES.NOT_FOUND.message } });
|
||||
return (0, import_response.makeResponse)({
|
||||
ctr,
|
||||
content: {
|
||||
code: 200,
|
||||
data: {
|
||||
id: preview.id,
|
||||
prNumber: preview.prNumber,
|
||||
prTitle: preview.prTitle,
|
||||
commitSha: preview.commitSha,
|
||||
status: preview.status,
|
||||
instanceIp: preview.instanceIp,
|
||||
port: preview.port,
|
||||
logs: preview.logs,
|
||||
createdAt: preview.createdAt,
|
||||
updatedAt: preview.updatedAt,
|
||||
stoppedAt: preview.stoppedAt,
|
||||
lastActivityAt: preview.lastActivityAt,
|
||||
repoOwner: preview.repoConfig.repoOwner,
|
||||
repoName: preview.repoConfig.repoName,
|
||||
jobs: preview.jobs.map((j) => ({
|
||||
id: j.id,
|
||||
type: j.type,
|
||||
status: j.status,
|
||||
createdAt: j.createdAt,
|
||||
startedAt: j.startedAt,
|
||||
finishedAt: j.finishedAt,
|
||||
error: j.error
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async function stopPreviewRoute(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const preview = await import_db.prisma.preview.findFirst({
|
||||
where: { id, repoConfig: { userId: user.id } }
|
||||
});
|
||||
if (!preview) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: import_errors.ERROR_MESSAGES.NOT_FOUND.message } });
|
||||
if (preview.status === "STOPPED") return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Already stopped" } });
|
||||
await import_db.prisma.job.create({
|
||||
data: { previewId: id, type: "STOP", status: "PENDING", payload: { reason: "Manual stop via UI" } }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Stop job enqueued" } });
|
||||
}
|
||||
async function previewLogsWs(ctr) {
|
||||
const auth = ctr.getAuth?.();
|
||||
if (!auth?.success) {
|
||||
ctr.close(1008, "Unauthorized");
|
||||
return;
|
||||
}
|
||||
const id = parseInt(ctr.params.get("id") || "0", 10);
|
||||
const preview = await import_db.prisma.preview.findFirst({
|
||||
where: { id, repoConfig: { userId: auth.user.id } }
|
||||
});
|
||||
if (!preview) {
|
||||
ctr.close(1008, "Not found");
|
||||
return;
|
||||
}
|
||||
await ctr.print(JSON.stringify({ type: "init", logs: preview.logs }));
|
||||
const unsub = (0, import_deploy.subscribeToLogs)(id, (text) => {
|
||||
try {
|
||||
ctr.print(JSON.stringify({ type: "append", text }));
|
||||
} catch {
|
||||
}
|
||||
});
|
||||
ctr.$abort(unsub);
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
getPreview,
|
||||
listPreviews,
|
||||
previewLogsWs,
|
||||
stopPreviewRoute
|
||||
});
|
||||
//# sourceMappingURL=previews.js.map
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../../src/routes/api/previews.ts"],
|
||||
"sourcesContent": ["import { prisma } from \"../../lib/db\";\nimport { makeResponse } from \"../../lib/response\";\nimport { ERROR_MESSAGES } from \"../../lib/errors\";\nimport { subscribeToLogs } from \"../../services/deploy\";\n\nfunction requireAuth(ctr: any) {\n const auth = ctr.getAuth?.();\n if (!auth?.success) return null;\n return auth.user;\n}\n\nexport async function listPreviews(ctr: any) {\n const user = requireAuth(ctr);\n if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });\n\n const previews = await prisma.preview.findMany({\n where: { repoConfig: { userId: user.id } },\n include: { repoConfig: { select: { repoOwner: true, repoName: true } } },\n orderBy: { updatedAt: \"desc\" },\n });\n\n return makeResponse({\n ctr, content: {\n code: 200, data: previews.map(p => ({\n id: p.id,\n prNumber: p.prNumber,\n prTitle: p.prTitle,\n commitSha: p.commitSha,\n status: p.status,\n instanceIp: p.instanceIp,\n port: p.port,\n createdAt: p.createdAt,\n updatedAt: p.updatedAt,\n lastActivityAt: p.lastActivityAt,\n repoOwner: p.repoConfig.repoOwner,\n repoName: p.repoConfig.repoName,\n }))\n }\n });\n}\n\nexport async function getPreview(ctr: any) {\n const user = requireAuth(ctr);\n if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });\n\n const id = parseInt(ctr.params.get(\"id\") || \"0\", 10);\n const preview = await prisma.preview.findFirst({\n where: { id, repoConfig: { userId: user.id } },\n include: {\n repoConfig: { select: { repoOwner: true, repoName: true } },\n jobs: { orderBy: { createdAt: \"desc\" }, take: 10 },\n },\n });\n\n if (!preview) return makeResponse({ ctr, content: { code: 404, message: ERROR_MESSAGES.NOT_FOUND.message } });\n\n return makeResponse({\n ctr, content: {\n code: 200, data: {\n id: preview.id,\n prNumber: preview.prNumber,\n prTitle: preview.prTitle,\n commitSha: preview.commitSha,\n status: preview.status,\n instanceIp: preview.instanceIp,\n port: preview.port,\n logs: preview.logs,\n createdAt: preview.createdAt,\n updatedAt: preview.updatedAt,\n stoppedAt: preview.stoppedAt,\n lastActivityAt: preview.lastActivityAt,\n repoOwner: preview.repoConfig.repoOwner,\n repoName: preview.repoConfig.repoName,\n jobs: preview.jobs.map(j => ({\n id: j.id,\n type: j.type,\n status: j.status,\n createdAt: j.createdAt,\n startedAt: j.startedAt,\n finishedAt: j.finishedAt,\n error: j.error,\n })),\n }\n }\n });\n}\n\nexport async function stopPreviewRoute(ctr: any) {\n const user = requireAuth(ctr);\n if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });\n\n const id = parseInt(ctr.params.get(\"id\") || \"0\", 10);\n const preview = await prisma.preview.findFirst({\n where: { id, repoConfig: { userId: user.id } },\n });\n\n if (!preview) return makeResponse({ ctr, content: { code: 404, message: ERROR_MESSAGES.NOT_FOUND.message } });\n if (preview.status === \"STOPPED\") return makeResponse({ ctr, content: { code: 400, message: \"Already stopped\" } });\n\n await prisma.job.create({\n data: { previewId: id, type: \"STOP\", status: \"PENDING\", payload: { reason: \"Manual stop via UI\" } },\n });\n\n return makeResponse({ ctr, content: { code: 200, message: \"Stop job enqueued\" } });\n}\n\nexport async function previewLogsWs(ctr: any) {\n const auth = ctr.getAuth?.();\n if (!auth?.success) {\n ctr.close(1008, \"Unauthorized\");\n return;\n }\n\n const id = parseInt(ctr.params.get(\"id\") || \"0\", 10);\n const preview = await prisma.preview.findFirst({\n where: { id, repoConfig: { userId: auth.user.id } },\n });\n\n if (!preview) {\n ctr.close(1008, \"Not found\");\n return;\n }\n\n await ctr.print(JSON.stringify({ type: \"init\", logs: preview.logs }));\n\n const unsub = subscribeToLogs(id, (text) => {\n try {\n ctr.print(JSON.stringify({ type: \"append\", text }));\n } catch {}\n });\n\n ctr.$abort(unsub);\n}\n"],
|
||||
"mappings": "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",
|
||||
"names": []
|
||||
}
|
||||
Vendored
+180
@@ -0,0 +1,180 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var repos_exports = {};
|
||||
__export(repos_exports, {
|
||||
getRepoConfig: () => getRepoConfig,
|
||||
listRepos: () => listRepos,
|
||||
saveRepoConfig: () => saveRepoConfig,
|
||||
toggleRepoEnabled: () => toggleRepoEnabled
|
||||
});
|
||||
module.exports = __toCommonJS(repos_exports);
|
||||
var import_db = require("../../lib/db");
|
||||
var import_response = require("../../lib/response");
|
||||
var import_errors = require("../../lib/errors");
|
||||
var import_gitea = require("../../services/gitea");
|
||||
var import_adminSettings = require("../../lib/adminSettings");
|
||||
var import_env = require("../../lib/env");
|
||||
function requireAuth(ctr) {
|
||||
const auth = ctr.getAuth?.();
|
||||
if (!auth?.success) return null;
|
||||
return auth.user;
|
||||
}
|
||||
async function listRepos(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const fullUser = await import_db.prisma.user.findUnique({ where: { id: user.id } });
|
||||
if (!fullUser?.giteaInstanceUrl || !fullUser?.giteaPAT) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Gitea credentials not configured" } });
|
||||
}
|
||||
const giteaRepos = await (0, import_gitea.fetchUserRepos)(fullUser);
|
||||
const configs = await import_db.prisma.repoConfig.findMany({ where: { userId: user.id } });
|
||||
const configMap = new Map(configs.map((c) => [`${c.repoOwner}/${c.repoName}`, c]));
|
||||
const allOwners = [...new Set(giteaRepos.map((r) => r.full_name?.split("/")[0]).filter(Boolean))];
|
||||
const allConfigs = await import_db.prisma.repoConfig.findMany({
|
||||
where: { repoOwner: { in: allOwners } },
|
||||
select: { repoOwner: true, repoName: true, userId: true }
|
||||
});
|
||||
const claimedByOthers = new Set(
|
||||
allConfigs.filter((c) => c.userId !== user.id).map((c) => `${c.repoOwner}/${c.repoName}`)
|
||||
);
|
||||
const result = giteaRepos.map((r) => {
|
||||
const [owner, name] = (r.full_name || "").split("/");
|
||||
const key = `${owner}/${name}`;
|
||||
const config = configMap.get(key);
|
||||
const { giteaWebhookId, ...safeConfig } = config || {};
|
||||
return {
|
||||
owner,
|
||||
name,
|
||||
fullName: r.full_name,
|
||||
htmlUrl: r.html_url,
|
||||
isEnabled: config?.isEnabled ?? false,
|
||||
claimedByOther: claimedByOthers.has(key),
|
||||
config: config ? safeConfig : null
|
||||
};
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: result } });
|
||||
}
|
||||
async function getRepoConfig(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const owner = ctr.params.get("owner");
|
||||
const repo = ctr.params.get("repo");
|
||||
const config = await import_db.prisma.repoConfig.findFirst({
|
||||
where: { repoOwner: owner, repoName: repo, userId: user.id }
|
||||
});
|
||||
if (!config) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: "Repo config not found" } });
|
||||
const { giteaWebhookId, ...safeConfig } = config;
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: safeConfig } });
|
||||
}
|
||||
async function saveRepoConfig(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { owner, repo, ...configData } = body || {};
|
||||
if (!owner || !repo) return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "owner and repo required" } });
|
||||
const existing = await import_db.prisma.repoConfig.findFirst({
|
||||
where: { repoOwner: owner, repoName: repo }
|
||||
});
|
||||
if (existing && existing.userId !== user.id) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 409, message: "This repo is already configured by another user." } });
|
||||
}
|
||||
const settings = await (0, import_adminSettings.getAdminSettings)();
|
||||
const sanitized = {
|
||||
repoOwner: owner,
|
||||
repoName: repo,
|
||||
userId: user.id,
|
||||
instanceType: configData.instanceType ?? settings.defaultInstanceType,
|
||||
inactivityHours: Math.min(72, Math.max(0.5, Number(configData.inactivityHours ?? 12))),
|
||||
port: Number(configData.port ?? 3e3),
|
||||
envVars: configData.envVars ?? {},
|
||||
useDockerCompose: Boolean(configData.useDockerCompose ?? false),
|
||||
composeFilePath: configData.composeFilePath ?? null,
|
||||
aptPackages: Array.isArray(configData.aptPackages) ? configData.aptPackages : [],
|
||||
setupCommands: Array.isArray(configData.setupCommands) ? configData.setupCommands : [],
|
||||
buildCommands: Array.isArray(configData.buildCommands) ? configData.buildCommands : [],
|
||||
postBuildCommands: Array.isArray(configData.postBuildCommands) ? configData.postBuildCommands : [],
|
||||
runCommand: configData.runCommand ?? null,
|
||||
denyList: Array.isArray(configData.denyList) ? configData.denyList : []
|
||||
};
|
||||
let config;
|
||||
if (existing) {
|
||||
config = await import_db.prisma.repoConfig.update({ where: { id: existing.id }, data: sanitized });
|
||||
} else {
|
||||
config = await import_db.prisma.repoConfig.create({ data: sanitized });
|
||||
}
|
||||
const { giteaWebhookId, ...safeConfig } = config;
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: safeConfig } });
|
||||
}
|
||||
async function toggleRepoEnabled(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { owner, repo, enabled } = body || {};
|
||||
if (!owner || !repo || enabled === void 0) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "owner, repo, enabled required" } });
|
||||
}
|
||||
const fullUser = await import_db.prisma.user.findUnique({ where: { id: user.id } });
|
||||
if (!fullUser?.giteaInstanceUrl || !fullUser?.giteaPAT) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Gitea credentials not configured" } });
|
||||
}
|
||||
let config = await import_db.prisma.repoConfig.findFirst({ where: { repoOwner: owner, repoName: repo } });
|
||||
if (config && config.userId !== user.id) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 409, message: "This repo is already configured by another user." } });
|
||||
}
|
||||
const webhookToken = await import_db.prisma.webhookToken.findUnique({ where: { userId: user.id } });
|
||||
if (!webhookToken) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "No webhook token configured. Go to Settings to generate one." } });
|
||||
}
|
||||
if (enabled) {
|
||||
if (!config) {
|
||||
config = await import_db.prisma.repoConfig.create({
|
||||
data: { repoOwner: owner, repoName: repo, userId: user.id, isEnabled: false }
|
||||
});
|
||||
}
|
||||
const webhookUrl = `${import_env.env.PP_BASE_URL}/webhook/${user.id}`;
|
||||
const hookId = await (0, import_gitea.registerWebhook)(fullUser, owner, repo, webhookUrl, webhookToken.token);
|
||||
await import_db.prisma.repoConfig.update({
|
||||
where: { id: config.id },
|
||||
data: { isEnabled: true, giteaWebhookId: String(hookId) }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Repo enabled and webhook registered" } });
|
||||
} else {
|
||||
if (!config) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: "Repo config not found" } });
|
||||
if (config.giteaWebhookId) {
|
||||
try {
|
||||
await (0, import_gitea.deleteWebhook)(fullUser, owner, repo, config.giteaWebhookId);
|
||||
} catch (e) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: `Failed to delete Gitea webhook: ${e.message}` } });
|
||||
}
|
||||
}
|
||||
await import_db.prisma.repoConfig.update({
|
||||
where: { id: config.id },
|
||||
data: { isEnabled: false, giteaWebhookId: null }
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Repo disabled and webhook removed" } });
|
||||
}
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
getRepoConfig,
|
||||
listRepos,
|
||||
saveRepoConfig,
|
||||
toggleRepoEnabled
|
||||
});
|
||||
//# sourceMappingURL=repos.js.map
|
||||
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+204
@@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var user_exports = {};
|
||||
__export(user_exports, {
|
||||
getUserSettings: () => getUserSettings,
|
||||
getWebhookSecret: () => getWebhookSecret,
|
||||
regenerateWebhookSecret: () => regenerateWebhookSecret,
|
||||
updateAws: () => updateAws,
|
||||
updateGitea: () => updateGitea,
|
||||
updatePassword: () => updatePassword,
|
||||
updateUsername: () => updateUsername
|
||||
});
|
||||
module.exports = __toCommonJS(user_exports);
|
||||
var import_bcryptjs = __toESM(require("bcryptjs"));
|
||||
var import_crypto = require("crypto");
|
||||
var import_db = require("../../lib/db");
|
||||
var import_response = require("../../lib/response");
|
||||
var import_errors = require("../../lib/errors");
|
||||
var import_encryption = require("../../lib/encryption");
|
||||
var import_gitea = require("../../services/gitea");
|
||||
var import_ec2 = require("../../services/ec2");
|
||||
var import_env = require("../../lib/env");
|
||||
function requireAuth(ctr) {
|
||||
const auth = ctr.getAuth?.();
|
||||
if (!auth?.success) return null;
|
||||
return auth.user;
|
||||
}
|
||||
async function getUserSettings(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: import_errors.ERROR_MESSAGES.UNAUTHORIZED.code, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const webhookToken = await import_db.prisma.webhookToken.findUnique({ where: { userId: user.id } });
|
||||
const fullUser = await import_db.prisma.user.findUnique({ where: { id: user.id } });
|
||||
return (0, import_response.makeResponse)({
|
||||
ctr,
|
||||
content: {
|
||||
code: 200,
|
||||
data: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
giteaUsername: fullUser?.giteaUsername,
|
||||
giteaInstanceUrl: fullUser?.giteaInstanceUrl,
|
||||
giteaPatSet: !!fullUser?.giteaPAT,
|
||||
awsAccessKeyId: fullUser?.awsAccessKeyId ? "****" : null,
|
||||
awsRegion: fullUser?.awsRegion,
|
||||
webhookUrl: `${import_env.env.PP_BASE_URL}/webhook/${user.id}`,
|
||||
webhookSecret: webhookToken?.token ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : null,
|
||||
webhookTokenExists: !!webhookToken
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async function updateUsername(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { username } = body || {};
|
||||
if (!username || typeof username !== "string") return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Username required" } });
|
||||
const existing = await import_db.prisma.user.findFirst({ where: { username, id: { not: user.id } } });
|
||||
if (existing) return (0, import_response.makeResponse)({ ctr, content: { code: 409, message: "Username already taken" } });
|
||||
await import_db.prisma.user.update({ where: { id: user.id }, data: { username } });
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Username updated" } });
|
||||
}
|
||||
async function updatePassword(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { currentPassword, newPassword } = body || {};
|
||||
if (!currentPassword || !newPassword) return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Current and new passwords required" } });
|
||||
const fullUser = await import_db.prisma.user.findUnique({ where: { id: user.id } });
|
||||
if (!fullUser) return (0, import_response.makeResponse)({ ctr, content: { code: 404, message: "User not found" } });
|
||||
const valid = await import_bcryptjs.default.compare(currentPassword, fullUser.passwordHash);
|
||||
if (!valid) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: "Current password incorrect" } });
|
||||
const hash = await import_bcryptjs.default.hash(newPassword, 12);
|
||||
await import_db.prisma.user.update({ where: { id: user.id }, data: { passwordHash: hash } });
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Password updated" } });
|
||||
}
|
||||
async function updateGitea(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { giteaInstanceUrl, giteaUsername, giteaPAT } = body || {};
|
||||
if (!giteaInstanceUrl || !giteaUsername) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "Gitea URL and username required" } });
|
||||
}
|
||||
const cleanUrl = giteaInstanceUrl.replace(/\/+$/, "");
|
||||
const validation = await (0, import_gitea.validateGiteaUrl)(cleanUrl);
|
||||
if (!validation.success) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: `Gitea validation failed: ${validation.error}` } });
|
||||
}
|
||||
const data = { giteaInstanceUrl: cleanUrl, giteaUsername };
|
||||
if (giteaPAT) data.giteaPAT = (0, import_encryption.encrypt)(giteaPAT);
|
||||
await import_db.prisma.user.update({ where: { id: user.id }, data });
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: `Connected to Gitea ${validation.version}`, data: { version: validation.version } } });
|
||||
}
|
||||
async function updateAws(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const body = await ctr.body();
|
||||
const { awsAccessKeyId, awsSecretAccessKey, awsRegion } = body || {};
|
||||
if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: "AWS credentials and region required" } });
|
||||
}
|
||||
const tempUser = {
|
||||
...user,
|
||||
awsAccessKeyId: (0, import_encryption.encrypt)(awsAccessKeyId),
|
||||
awsSecretAccessKey: (0, import_encryption.encrypt)(awsSecretAccessKey),
|
||||
awsRegion
|
||||
};
|
||||
const validation = await (0, import_ec2.validateAwsCredentials)(tempUser);
|
||||
if (!validation.success) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 400, message: `AWS validation failed: ${validation.error}` } });
|
||||
}
|
||||
await import_db.prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
awsAccessKeyId: (0, import_encryption.encrypt)(awsAccessKeyId),
|
||||
awsSecretAccessKey: (0, import_encryption.encrypt)(awsSecretAccessKey),
|
||||
awsRegion
|
||||
}
|
||||
});
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: `Connected as ${validation.arn}`, data: { arn: validation.arn } } });
|
||||
}
|
||||
async function getWebhookSecret(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
let token = await import_db.prisma.webhookToken.findUnique({ where: { userId: user.id } });
|
||||
if (!token) {
|
||||
const secret = (0, import_crypto.randomBytes)(32).toString("hex");
|
||||
token = await import_db.prisma.webhookToken.create({ data: { userId: user.id, token: secret } });
|
||||
}
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, data: { token: token.token } } });
|
||||
}
|
||||
async function regenerateWebhookSecret(ctr) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return (0, import_response.makeResponse)({ ctr, content: { code: 401, message: import_errors.ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
const newSecret = (0, import_crypto.randomBytes)(32).toString("hex");
|
||||
await import_db.prisma.webhookToken.upsert({
|
||||
where: { userId: user.id },
|
||||
update: { token: newSecret },
|
||||
create: { userId: user.id, token: newSecret }
|
||||
});
|
||||
const fullUser = await import_db.prisma.user.findUnique({ where: { id: user.id } });
|
||||
if (!fullUser?.giteaInstanceUrl) {
|
||||
return (0, import_response.makeResponse)({ ctr, content: { code: 200, message: "Secret regenerated (no Gitea hooks to update)", data: { token: newSecret } } });
|
||||
}
|
||||
const configs = await import_db.prisma.repoConfig.findMany({
|
||||
where: { userId: user.id, giteaWebhookId: { not: null } }
|
||||
});
|
||||
const { updateWebhookSecret } = await import("../../services/gitea");
|
||||
const webhookUrl = `${import_env.env.PP_BASE_URL}/webhook/${user.id}`;
|
||||
const errors = [];
|
||||
for (const config of configs) {
|
||||
try {
|
||||
await updateWebhookSecret(fullUser, config.repoOwner, config.repoName, config.giteaWebhookId, webhookUrl, newSecret);
|
||||
} catch (e) {
|
||||
errors.push(`${config.repoOwner}/${config.repoName}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
return (0, import_response.makeResponse)({
|
||||
ctr,
|
||||
content: {
|
||||
code: 200,
|
||||
message: errors.length ? `Secret regenerated with ${errors.length} hook update errors` : "Secret regenerated and all hooks updated",
|
||||
data: { token: newSecret, errors }
|
||||
}
|
||||
});
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
getUserSettings,
|
||||
getWebhookSecret,
|
||||
regenerateWebhookSecret,
|
||||
updateAws,
|
||||
updateGitea,
|
||||
updatePassword,
|
||||
updateUsername
|
||||
});
|
||||
//# sourceMappingURL=user.js.map
|
||||
Vendored
+7
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user