162 lines
4.2 KiB
TypeScript
162 lines
4.2 KiB
TypeScript
import { type } from "arktype";
|
|
|
|
export const REPOSITORY_BACKENDS = {
|
|
local: "local",
|
|
s3: "s3",
|
|
r2: "r2",
|
|
gcs: "gcs",
|
|
azure: "azure",
|
|
rclone: "rclone",
|
|
rest: "rest",
|
|
sftp: "sftp",
|
|
} as const;
|
|
|
|
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
|
|
|
|
// Common fields for all repository configs
|
|
const baseRepositoryConfigSchema = type({
|
|
isExistingRepository: "boolean?",
|
|
customPassword: "string?",
|
|
});
|
|
|
|
export const s3RepositoryConfigSchema = type({
|
|
backend: "'s3'",
|
|
endpoint: "string",
|
|
bucket: "string",
|
|
accessKeyId: "string",
|
|
secretAccessKey: "string",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const r2RepositoryConfigSchema = type({
|
|
backend: "'r2'",
|
|
endpoint: "string",
|
|
bucket: "string",
|
|
accessKeyId: "string",
|
|
secretAccessKey: "string",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const localRepositoryConfigSchema = type({
|
|
backend: "'local'",
|
|
name: "string",
|
|
path: "string?",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const gcsRepositoryConfigSchema = type({
|
|
backend: "'gcs'",
|
|
bucket: "string",
|
|
projectId: "string",
|
|
credentialsJson: "string",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const azureRepositoryConfigSchema = type({
|
|
backend: "'azure'",
|
|
container: "string",
|
|
accountName: "string",
|
|
accountKey: "string",
|
|
endpointSuffix: "string?",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const rcloneRepositoryConfigSchema = type({
|
|
backend: "'rclone'",
|
|
remote: "string",
|
|
path: "string",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
const pemCertificateType = type("string").narrow((value, ctx) => {
|
|
if (!value || value.trim() === "") {
|
|
return true;
|
|
}
|
|
|
|
const trimmed = value.trim();
|
|
|
|
// Check for BEGIN and END markers
|
|
if (!trimmed.includes("-----BEGIN CERTIFICATE-----") || !trimmed.includes("-----END CERTIFICATE-----")) {
|
|
return ctx.error("Certificate must be in PEM format with BEGIN and END markers");
|
|
}
|
|
|
|
// Extract content between markers
|
|
const beginMarker = "-----BEGIN CERTIFICATE-----";
|
|
const endMarker = "-----END CERTIFICATE-----";
|
|
const beginIndex = trimmed.indexOf(beginMarker);
|
|
const endIndex = trimmed.indexOf(endMarker);
|
|
|
|
if (beginIndex === -1 || endIndex === -1 || endIndex <= beginIndex) {
|
|
return ctx.error("Invalid PEM certificate structure");
|
|
}
|
|
|
|
// Extract base64 content
|
|
const base64Content = trimmed.substring(beginIndex + beginMarker.length, endIndex).replace(/\s/g, "");
|
|
|
|
// Validate base64 format
|
|
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
if (!base64Regex.test(base64Content)) {
|
|
return ctx.error("Certificate contains invalid base64 characters");
|
|
}
|
|
|
|
if (base64Content.length === 0) {
|
|
return ctx.error("Certificate content is empty");
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
const optionalPemCertificate = pemCertificateType.optional();
|
|
|
|
export const restRepositoryConfigSchema = type({
|
|
backend: "'rest'",
|
|
url: "string",
|
|
username: "string?",
|
|
password: "string?",
|
|
path: "string?",
|
|
cacert: optionalPemCertificate as any,
|
|
insecureTls: "boolean?",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const sftpRepositoryConfigSchema = type({
|
|
backend: "'sftp'",
|
|
host: "string",
|
|
port: type("string.integer").or(type("number")).to("1 <= number <= 65535").default(22),
|
|
user: "string",
|
|
path: "string",
|
|
privateKey: "string",
|
|
skipHostKeyCheck: "boolean = true",
|
|
knownHosts: "string?",
|
|
}).and(baseRepositoryConfigSchema);
|
|
|
|
export const repositoryConfigSchemaBase = s3RepositoryConfigSchema
|
|
.or(r2RepositoryConfigSchema)
|
|
.or(localRepositoryConfigSchema)
|
|
.or(gcsRepositoryConfigSchema)
|
|
.or(azureRepositoryConfigSchema)
|
|
.or(rcloneRepositoryConfigSchema)
|
|
.or(restRepositoryConfigSchema)
|
|
.or(sftpRepositoryConfigSchema);
|
|
|
|
export const repositoryConfigSchema = repositoryConfigSchemaBase.onUndeclaredKey("delete");
|
|
|
|
export type RepositoryConfig = typeof repositoryConfigSchema.infer;
|
|
|
|
export const COMPRESSION_MODES = {
|
|
off: "off",
|
|
auto: "auto",
|
|
max: "max",
|
|
} as const;
|
|
|
|
export type CompressionMode = keyof typeof COMPRESSION_MODES;
|
|
|
|
export const REPOSITORY_STATUS = {
|
|
healthy: "healthy",
|
|
error: "error",
|
|
unknown: "unknown",
|
|
} as const;
|
|
|
|
export type RepositoryStatus = keyof typeof REPOSITORY_STATUS;
|
|
|
|
export const OVERWRITE_MODES = {
|
|
always: "always",
|
|
ifChanged: "if-changed",
|
|
ifNewer: "if-newer",
|
|
never: "never",
|
|
} as const;
|
|
|
|
export type OverwriteMode = (typeof OVERWRITE_MODES)[keyof typeof OVERWRITE_MODES];
|