Really huge mass update; Getting everything up-to-spec and implementing a wide range of features
Deploy / Build (pull_request) Successful in 40s
Deploy / Build and Push Docker Image (pull_request) Has been skipped

This commit is contained in:
2026-07-26 14:24:18 +02:00
parent 2c563685bd
commit 8b53698f29
72 changed files with 4275 additions and 693 deletions
+64 -28
View File
@@ -4,7 +4,7 @@ import { prisma } from "../../lib/db";
import { makeResponse } from "../../lib/response";
import { ERROR_MESSAGES } from "../../lib/errors";
import { encrypt, decrypt } from "../../lib/encryption";
import { validateGiteaUrl } from "../../services/gitea";
import { updateWebhookSecret, validateGiteaUrl, validateGiteaToken } from "../../services/gitea";
import { validateAwsCredentials } from "../../services/ec2";
import { env } from "../../lib/env";
@@ -14,6 +14,39 @@ function requireAuth(ctr: any) {
return auth.user;
}
async function getOrCreateWebhookToken(userId: number) {
const existing = await prisma.webhookToken.findUnique({ where: { userId } });
if (existing) return existing;
const secret = randomBytes(32).toString("hex");
return prisma.webhookToken.create({ data: { userId, token: secret } });
}
async function syncRegisteredRepoWebhooks(userId: number, fullUser: any, secret: string) {
if (!fullUser?.giteaInstanceUrl || !fullUser?.giteaPAT) {
return { updated: 0, errors: [] as string[] };
}
const configs = await prisma.repoConfig.findMany({
where: { userId, giteaWebhookId: { not: null } },
});
const webhookUrl = `${env.PP_BASE_URL}/webhook/${userId}`;
const errors: string[] = [];
let updated = 0;
for (const config of configs) {
try {
await updateWebhookSecret(fullUser, config.repoOwner, config.repoName, config.giteaWebhookId!, webhookUrl, secret);
updated++;
} catch (e: any) {
errors.push(`${config.repoOwner}/${config.repoName}: ${e.message}`);
}
}
return { updated, errors };
}
export async function getUserSettings(ctr: any) {
const user = requireAuth(ctr);
if (!user) return makeResponse({ ctr, content: { code: ERROR_MESSAGES.UNAUTHORIZED.code, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
@@ -88,17 +121,38 @@ export async function updateGitea(ctr: any) {
}
const data: any = { giteaInstanceUrl: cleanUrl, giteaUsername };
if (giteaPAT) data.giteaPAT = encrypt(giteaPAT);
if (giteaPAT) {
const tokenCheck = await validateGiteaToken(cleanUrl, giteaPAT, giteaUsername);
if (!tokenCheck.success) {
return makeResponse({ ctr, content: { code: 400, message: `Gitea token rejected: ${tokenCheck.error}` } });
}
data.giteaPAT = encrypt(giteaPAT);
}
await prisma.user.update({ where: { id: user.id }, data });
return makeResponse({ ctr, content: { code: 200, message: `Connected to Gitea ${validation.version}`, data: { version: validation.version } } });
const updatedUser = await prisma.user.update({ where: { id: user.id }, data });
const webhookToken = await getOrCreateWebhookToken(user.id);
const hookSync = await syncRegisteredRepoWebhooks(user.id, updatedUser, webhookToken.token);
return makeResponse({
ctr, content: {
code: 200,
message: hookSync.errors.length
? `Connected to Gitea ${validation.version}; ${hookSync.updated} webhooks updated, ${hookSync.errors.length} failed`
: hookSync.updated > 0
? `Connected to Gitea ${validation.version}; ${hookSync.updated} webhooks updated`
: `Connected to Gitea ${validation.version}`,
data: { version: validation.version, hookSync },
}
});
}
export async function updateAws(ctr: any) {
const user = requireAuth(ctr);
if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
const body = await ctr.body();
const { awsAccessKeyId, awsSecretAccessKey, awsRegion } = body || {};
const awsAccessKeyId = typeof body?.awsAccessKeyId === "string" ? body.awsAccessKeyId.trim() : body?.awsAccessKeyId;
const awsSecretAccessKey = typeof body?.awsSecretAccessKey === "string" ? body.awsSecretAccessKey.trim() : body?.awsSecretAccessKey;
const awsRegion = typeof body?.awsRegion === "string" ? body.awsRegion.trim() : body?.awsRegion;
if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) {
return makeResponse({ ctr, content: { code: 400, message: "AWS credentials and region required" } });
@@ -131,13 +185,9 @@ export async function getWebhookSecret(ctr: any) {
const user = requireAuth(ctr);
if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
let token = await prisma.webhookToken.findUnique({ where: { userId: user.id } });
if (!token) {
const secret = randomBytes(32).toString("hex");
token = await prisma.webhookToken.create({ data: { userId: user.id, token: secret } });
}
const token = await getOrCreateWebhookToken(user.id);
return makeResponse({ ctr, content: { code: 200, data: { token: token.token } } });
return makeResponse({ ctr, content: { code: 200, data: { token: token.token, webhookUrl: `${env.PP_BASE_URL}/webhook/${user.id}` } } });
}
export async function regenerateWebhookSecret(ctr: any) {
@@ -156,27 +206,13 @@ export async function regenerateWebhookSecret(ctr: any) {
return makeResponse({ ctr, content: { code: 200, message: "Secret regenerated (no Gitea hooks to update)", data: { token: newSecret } } });
}
const configs = await prisma.repoConfig.findMany({
where: { userId: user.id, giteaWebhookId: { not: null } },
});
const { updateWebhookSecret } = await import("../../services/gitea");
const webhookUrl = `${env.PP_BASE_URL}/webhook/${user.id}`;
const errors: string[] = [];
for (const config of configs) {
try {
await updateWebhookSecret(fullUser as any, config.repoOwner, config.repoName, config.giteaWebhookId!, webhookUrl, newSecret);
} catch (e: any) {
errors.push(`${config.repoOwner}/${config.repoName}: ${e.message}`);
}
}
const hookSync = await syncRegisteredRepoWebhooks(user.id, fullUser, newSecret);
return 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 },
message: hookSync.errors.length ? `Secret regenerated with ${hookSync.errors.length} hook update errors` : "Secret regenerated and all hooks updated",
data: { token: newSecret, errors: hookSync.errors, hookSync },
}
});
}