Files
homelab/apps/harness/src/lib/credentials.ts
Julia McGhee a687652bcd
Some checks failed
CI / lint-and-test (push) Successful in 30s
CI / build (push) Has been cancelled
Deploy Production / deploy (push) Has been cancelled
Add Gitea as a git provider for harness workspace repositories
Support Gitea alongside GitHub/GitLab for repo search, authenticated
cloning, and pull request creation via Gitea API. Tasks can specify
gitProvider and gitBaseUrl in their spec (defaults to github for
backwards compat). Auto-discovers GITEA_TOKEN from env on boot.
2026-03-21 20:33:35 +00:00

87 lines
3.0 KiB
TypeScript

import { eq } from "drizzle-orm";
import { db } from "./db";
import { credentials as credentialsTable } from "@homelab/db";
export type Provider =
| "github" | "gitlab" | "gitea"
| "anthropic" | "openai" | "openrouter" | "google" | "opencode-zen" | "opencode-go";
export const GIT_PROVIDERS: Provider[] = ["github", "gitlab", "gitea"];
export const AI_PROVIDERS: Provider[] = ["anthropic", "openai", "openrouter", "google", "opencode-zen", "opencode-go"];
export interface Credential {
id: string;
provider: Provider;
label: string;
token: string;
baseUrl?: string;
}
function maskToken(token: string): string {
if (token.length <= 8) return "••••••••";
return token.slice(0, 4) + "••••" + token.slice(-4);
}
function rowToCredential(row: typeof credentialsTable.$inferSelect): Credential {
return {
id: row.id,
provider: row.provider as Provider,
label: row.label,
token: row.token,
baseUrl: row.baseUrl ?? undefined,
};
}
export async function getAllCredentials(): Promise<Credential[]> {
const rows = await db.select().from(credentialsTable);
return rows.map(r => ({ ...rowToCredential(r), token: maskToken(r.token) }));
}
export async function getCredentialsByKind(kind: "git" | "ai"): Promise<Credential[]> {
const providers = kind === "git" ? GIT_PROVIDERS : AI_PROVIDERS;
const rows = await db.select().from(credentialsTable);
return rows
.filter(r => providers.includes(r.provider as Provider))
.map(r => ({ ...rowToCredential(r), token: maskToken(r.token) }));
}
export async function getCredential(id: string): Promise<Credential | undefined> {
const [row] = await db.select().from(credentialsTable).where(eq(credentialsTable.id, id));
return row ? rowToCredential(row) : undefined;
}
export async function getCredentialsByProvider(provider: Provider): Promise<Credential[]> {
const rows = await db.select().from(credentialsTable).where(eq(credentialsTable.provider, provider));
return rows.map(r => ({ ...rowToCredential(r), token: maskToken(r.token) }));
}
export async function getRawCredentialsByProvider(provider: Provider): Promise<Credential[]> {
const rows = await db.select().from(credentialsTable).where(eq(credentialsTable.provider, provider));
return rows.map(rowToCredential);
}
export async function upsertCredential(cred: Credential): Promise<Credential> {
await db.insert(credentialsTable).values({
id: cred.id,
provider: cred.provider,
label: cred.label,
token: cred.token,
baseUrl: cred.baseUrl,
}).onConflictDoUpdate({
target: credentialsTable.id,
set: {
provider: cred.provider,
label: cred.label,
token: cred.token,
baseUrl: cred.baseUrl,
updatedAt: new Date(),
},
});
return { ...cred, token: maskToken(cred.token) };
}
export async function deleteCredential(id: string): Promise<boolean> {
const result = await db.delete(credentialsTable).where(eq(credentialsTable.id, id));
return (result as unknown as { rowCount: number }).rowCount > 0;
}