refactor(schemas): move restic schemas to a subfolder

This commit is contained in:
Nicolas Meienberger
2025-10-21 19:57:04 +02:00
parent e07c22a7d4
commit 0090c3c43c
5 changed files with 80 additions and 49 deletions

View File

@@ -1,12 +1,10 @@
import type { BackendStatus, BackendType, volumeConfigSchema } from "@ironmount/schemas";
import type {
BackendStatus,
BackendType,
CompressionMode,
RepositoryBackend,
RepositoryStatus,
repositoryConfigSchema,
volumeConfigSchema,
} from "@ironmount/schemas";
RepositoryStatus,
} from "@ironmount/schemas/restic";
import { sql } from "drizzle-orm";
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";

View File

@@ -1,7 +1,7 @@
import crypto from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import type { RepositoryConfig } from "@ironmount/schemas";
import type { RepositoryConfig } from "@ironmount/schemas/restic";
import { type } from "arktype";
import { $ } from "bun";
import { RESTIC_PASS_FILE } from "../core/constants";
@@ -38,6 +38,8 @@ const ensurePassfile = async () => {
const buildRepoUrl = (config: RepositoryConfig): string => {
switch (config.backend) {
case "local":
return config.path;
case "s3":
return `s3:${config.endpoint}/${config.bucket}`;
default: {
@@ -47,7 +49,9 @@ const buildRepoUrl = (config: RepositoryConfig): string => {
};
const buildEnv = async (config: RepositoryConfig) => {
const env: Record<string, string> = {};
const env: Record<string, string> = {
RESTIC_PASSWORD_FILE: RESTIC_PASS_FILE,
};
switch (config.backend) {
case "s3":
@@ -65,7 +69,7 @@ const init = async (config: RepositoryConfig) => {
const repoUrl = buildRepoUrl(config);
const env = await buildEnv(config);
const res = await $`restic init --repo ${repoUrl} --password-file ${RESTIC_PASS_FILE} --json`.env(env).nothrow();
const res = await $`restic init --repo ${repoUrl} --json`.env(env).nothrow();
if (res.exitCode !== 0) {
logger.error(`Restic init failed: ${res.stderr}`);
@@ -80,9 +84,7 @@ const backup = async (config: RepositoryConfig, source: string) => {
const repoUrl = buildRepoUrl(config);
const env = await buildEnv(config);
const res = await $`restic --repo ${repoUrl} backup ${source} --password-file /data/secrets/restic.pass --json`
.env(env)
.nothrow();
const res = await $`restic --repo ${repoUrl} backup ${source} --json`.env(env).nothrow();
if (res.exitCode !== 0) {
logger.error(`Restic backup failed: ${res.stderr}`);
@@ -99,8 +101,23 @@ const backup = async (config: RepositoryConfig, source: string) => {
return result;
};
const restore = async (config: RepositoryConfig, snapshotId: string, target: string) => {
const repoUrl = buildRepoUrl(config);
const env = await buildEnv(config);
const res = await $`restic --repo ${repoUrl} restore ${snapshotId} --target ${target} --json`.env(env).nothrow();
if (res.exitCode !== 0) {
logger.error(`Restic restore failed: ${res.stderr}`);
throw new Error(`Restic restore failed: ${res.stderr}`);
}
logger.info(`Restic restore completed for snapshot ${snapshotId} to target ${target}`);
};
export const restic = {
ensurePassfile,
init,
backup,
restore,
};

View File

@@ -4,6 +4,16 @@
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts"
},
"./restic": {
"import": "./src/restic.ts",
"require": "./src/restic.ts"
}
},
"type": "module",
"peerDependencies": {
"arktype": ">=2"

View File

@@ -53,41 +53,3 @@ export const BACKEND_STATUS = {
} as const;
export type BackendStatus = keyof typeof BACKEND_STATUS;
export const REPOSITORY_BACKENDS = {
local: "local",
sftp: "sftp",
s3: "s3",
} as const;
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
export const s3RepositoryConfigSchema = type({
backend: "'s3'",
endpoint: "string",
bucket: "string",
accessKeyId: "string",
secretAccessKey: "string",
});
export const repositoryConfigSchema = s3RepositoryConfigSchema;
export type RepositoryConfig = typeof repositoryConfigSchema.infer;
export const COMPRESSION_MODES = {
off: "off",
auto: "auto",
fastest: "fastest",
better: "better",
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;

View File

@@ -0,0 +1,44 @@
import { type } from "arktype";
export const REPOSITORY_BACKENDS = {
local: "local",
sftp: "sftp",
s3: "s3",
} as const;
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
export const s3RepositoryConfigSchema = type({
backend: "'s3'",
endpoint: "string",
bucket: "string",
accessKeyId: "string",
secretAccessKey: "string",
});
export const localRepositoryConfigSchema = type({
backend: "'local'",
path: "string",
});
export const repositoryConfigSchema = s3RepositoryConfigSchema.or(localRepositoryConfigSchema);
export type RepositoryConfig = typeof repositoryConfigSchema.infer;
export const COMPRESSION_MODES = {
off: "off",
auto: "auto",
fastest: "fastest",
better: "better",
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;