a71f801c3f
- 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>
181 lines
8.8 KiB
JavaScript
181 lines
8.8 KiB
JavaScript
"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
|