mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
feat: mirror repositories (#95)
* feat: mirror repositories feat: mirror backup repositories * chore: pr feedbacks
This commit is contained in:
committed by
Nicolas Meienberger
parent
780fdae63e
commit
9120153375
@@ -753,7 +753,7 @@ export const updateScheduleNotificationsMutation = (options?: Partial<Options<Up
|
|||||||
return mutationOptions;
|
return mutationOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getScheduleMirrorsQueryKey = (options: Options<GetScheduleMirrorsData>) => createQueryKey('getScheduleMirrors', options);
|
export const getScheduleMirrorsQueryKey = (options: Options<GetScheduleMirrorsData>) => createQueryKey("getScheduleMirrors", options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get mirror repository assignments for a backup schedule
|
* Get mirror repository assignments for a backup schedule
|
||||||
@@ -788,7 +788,7 @@ export const updateScheduleMirrorsMutation = (options?: Partial<Options<UpdateSc
|
|||||||
return mutationOptions;
|
return mutationOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMirrorCompatibilityQueryKey = (options: Options<GetMirrorCompatibilityData>) => createQueryKey('getMirrorCompatibility', options);
|
export const getMirrorCompatibilityQueryKey = (options: Options<GetMirrorCompatibilityData>) => createQueryKey("getMirrorCompatibility", options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get mirror compatibility info for all repositories relative to a backup schedule's primary repository
|
* Get mirror compatibility info for all repositories relative to a backup schedule's primary repository
|
||||||
@@ -806,7 +806,7 @@ export const getMirrorCompatibilityOptions = (options: Options<GetMirrorCompatib
|
|||||||
queryKey: getMirrorCompatibilityQueryKey(options)
|
queryKey: getMirrorCompatibilityQueryKey(options)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const listNotificationDestinationsQueryKey = (options?: Options<ListNotificationDestinationsData>) => createQueryKey('listNotificationDestinations', options);
|
export const listNotificationDestinationsQueryKey = (options?: Options<ListNotificationDestinationsData>) => createQueryKey("listNotificationDestinations", options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all notification destinations
|
* List all notification destinations
|
||||||
|
|||||||
@@ -329,6 +329,40 @@ export const updateScheduleMirrors = <ThrowOnError extends boolean = false>(opti
|
|||||||
*/
|
*/
|
||||||
export const getMirrorCompatibility = <ThrowOnError extends boolean = false>(options: Options<GetMirrorCompatibilityData, ThrowOnError>) => (options.client ?? client).get<GetMirrorCompatibilityResponses, unknown, ThrowOnError>({ url: '/api/v1/backups/{scheduleId}/mirrors/compatibility', ...options });
|
export const getMirrorCompatibility = <ThrowOnError extends boolean = false>(options: Options<GetMirrorCompatibilityData, ThrowOnError>) => (options.client ?? client).get<GetMirrorCompatibilityResponses, unknown, ThrowOnError>({ url: '/api/v1/backups/{scheduleId}/mirrors/compatibility', ...options });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mirror repository assignments for a backup schedule
|
||||||
|
*/
|
||||||
|
export const getScheduleMirrors = <ThrowOnError extends boolean = false>(options: Options<GetScheduleMirrorsData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? client).get<GetScheduleMirrorsResponses, unknown, ThrowOnError>({
|
||||||
|
url: '/api/v1/backups/{scheduleId}/mirrors',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update mirror repository assignments for a backup schedule
|
||||||
|
*/
|
||||||
|
export const updateScheduleMirrors = <ThrowOnError extends boolean = false>(options: Options<UpdateScheduleMirrorsData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? client).put<UpdateScheduleMirrorsResponses, unknown, ThrowOnError>({
|
||||||
|
url: '/api/v1/backups/{scheduleId}/mirrors',
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mirror compatibility info for all repositories relative to a backup schedule's primary repository
|
||||||
|
*/
|
||||||
|
export const getMirrorCompatibility = <ThrowOnError extends boolean = false>(options: Options<GetMirrorCompatibilityData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? client).get<GetMirrorCompatibilityResponses, unknown, ThrowOnError>({
|
||||||
|
url: '/api/v1/backups/{scheduleId}/mirrors/compatibility',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all notification destinations
|
* List all notification destinations
|
||||||
*/
|
*/
|
||||||
|
|||||||
139
app/drizzle/0018_bizarre_zzzax.sql
Normal file
139
app/drizzle/0018_bizarre_zzzax.sql
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
DROP TABLE IF EXISTS `backup_schedule_mirrors_table`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `backup_schedule_mirrors_table` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`schedule_id` integer NOT NULL,
|
||||||
|
`repository_id` text NOT NULL,
|
||||||
|
`enabled` integer DEFAULT true NOT NULL,
|
||||||
|
`last_copy_at` integer,
|
||||||
|
`last_copy_status` text,
|
||||||
|
`last_copy_error` text,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
FOREIGN KEY (`schedule_id`) REFERENCES `backup_schedules_table`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`repository_id`) REFERENCES `repositories_table`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_app_metadata` (
|
||||||
|
`key` text PRIMARY KEY NOT NULL,
|
||||||
|
`value` text NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_app_metadata`("key", "value", "created_at", "updated_at") SELECT "key", "value", "created_at", "updated_at" FROM `app_metadata`;--> statement-breakpoint
|
||||||
|
DROP TABLE `app_metadata`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_app_metadata` RENAME TO `app_metadata`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_backup_schedule_notifications_table` (
|
||||||
|
`schedule_id` integer NOT NULL,
|
||||||
|
`destination_id` integer NOT NULL,
|
||||||
|
`notify_on_start` integer DEFAULT false NOT NULL,
|
||||||
|
`notify_on_success` integer DEFAULT false NOT NULL,
|
||||||
|
`notify_on_failure` integer DEFAULT true NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
PRIMARY KEY(`schedule_id`, `destination_id`),
|
||||||
|
FOREIGN KEY (`schedule_id`) REFERENCES `backup_schedules_table`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`destination_id`) REFERENCES `notification_destinations_table`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_backup_schedule_notifications_table`("schedule_id", "destination_id", "notify_on_start", "notify_on_success", "notify_on_failure", "created_at") SELECT "schedule_id", "destination_id", "notify_on_start", "notify_on_success", "notify_on_failure", "created_at" FROM `backup_schedule_notifications_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `backup_schedule_notifications_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_backup_schedule_notifications_table` RENAME TO `backup_schedule_notifications_table`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_backup_schedules_table` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`volume_id` integer NOT NULL,
|
||||||
|
`repository_id` text NOT NULL,
|
||||||
|
`enabled` integer DEFAULT true NOT NULL,
|
||||||
|
`cron_expression` text NOT NULL,
|
||||||
|
`retention_policy` text,
|
||||||
|
`exclude_patterns` text DEFAULT '[]',
|
||||||
|
`include_patterns` text DEFAULT '[]',
|
||||||
|
`last_backup_at` integer,
|
||||||
|
`last_backup_status` text,
|
||||||
|
`last_backup_error` text,
|
||||||
|
`next_backup_at` integer,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
FOREIGN KEY (`volume_id`) REFERENCES `volumes_table`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`repository_id`) REFERENCES `repositories_table`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_backup_schedules_table`("id", "volume_id", "repository_id", "enabled", "cron_expression", "retention_policy", "exclude_patterns", "include_patterns", "last_backup_at", "last_backup_status", "last_backup_error", "next_backup_at", "created_at", "updated_at") SELECT "id", "volume_id", "repository_id", "enabled", "cron_expression", "retention_policy", "exclude_patterns", "include_patterns", "last_backup_at", "last_backup_status", "last_backup_error", "next_backup_at", "created_at", "updated_at" FROM `backup_schedules_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `backup_schedules_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_backup_schedules_table` RENAME TO `backup_schedules_table`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_notification_destinations_table` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`enabled` integer DEFAULT true NOT NULL,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
`config` text NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_notification_destinations_table`("id", "name", "enabled", "type", "config", "created_at", "updated_at") SELECT "id", "name", "enabled", "type", "config", "created_at", "updated_at" FROM `notification_destinations_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `notification_destinations_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_notification_destinations_table` RENAME TO `notification_destinations_table`;--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `notification_destinations_table_name_unique` ON `notification_destinations_table` (`name`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_repositories_table` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`short_id` text NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
`config` text NOT NULL,
|
||||||
|
`compression_mode` text DEFAULT 'auto',
|
||||||
|
`status` text DEFAULT 'unknown',
|
||||||
|
`last_checked` integer,
|
||||||
|
`last_error` text,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_repositories_table`("id", "short_id", "name", "type", "config", "compression_mode", "status", "last_checked", "last_error", "created_at", "updated_at") SELECT "id", "short_id", "name", "type", "config", "compression_mode", "status", "last_checked", "last_error", "created_at", "updated_at" FROM `repositories_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `repositories_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_repositories_table` RENAME TO `repositories_table`;--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `repositories_table_short_id_unique` ON `repositories_table` (`short_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `repositories_table_name_unique` ON `repositories_table` (`name`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_sessions_table` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`user_id` integer NOT NULL,
|
||||||
|
`expires_at` integer NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users_table`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_sessions_table`("id", "user_id", "expires_at", "created_at") SELECT "id", "user_id", "expires_at", "created_at" FROM `sessions_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `sessions_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_sessions_table` RENAME TO `sessions_table`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_users_table` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`username` text NOT NULL,
|
||||||
|
`password_hash` text NOT NULL,
|
||||||
|
`has_downloaded_restic_password` integer DEFAULT false NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_users_table`("id", "username", "password_hash", "has_downloaded_restic_password", "created_at", "updated_at") SELECT "id", "username", "password_hash", "has_downloaded_restic_password", "created_at", "updated_at" FROM `users_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `users_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_users_table` RENAME TO `users_table`;--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_table_username_unique` ON `users_table` (`username`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_volumes_table` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`short_id` text NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
`status` text DEFAULT 'unmounted' NOT NULL,
|
||||||
|
`last_error` text,
|
||||||
|
`last_health_check` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||||
|
`config` text NOT NULL,
|
||||||
|
`auto_remount` integer DEFAULT true NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_volumes_table`("id", "short_id", "name", "type", "status", "last_error", "last_health_check", "created_at", "updated_at", "config", "auto_remount") SELECT "id", "short_id", "name", "type", "status", "last_error", "last_health_check", "created_at", "updated_at", "config", "auto_remount" FROM `volumes_table`;--> statement-breakpoint
|
||||||
|
DROP TABLE `volumes_table`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_volumes_table` RENAME TO `volumes_table`;--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `volumes_table_short_id_unique` ON `volumes_table` (`short_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `volumes_table_name_unique` ON `volumes_table` (`name`);
|
||||||
|
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||||
1
app/drizzle/0019_heavy_shen.sql
Normal file
1
app/drizzle/0019_heavy_shen.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CREATE UNIQUE INDEX `backup_schedule_mirrors_table_schedule_id_repository_id_unique` ON `backup_schedule_mirrors_table` (`schedule_id`,`repository_id`);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"id": "b5b3acff-51d7-45ae-b9d2-4b07a6286fc3",
|
"id": "dedfb246-68e7-4590-af52-6476eb2999d1",
|
||||||
"prevId": "d5a60aea-4490-423e-8725-6ace87a76c9b",
|
"prevId": "121ef03c-eb5a-4b97-b2f1-4add6adfb080",
|
||||||
"tables": {
|
"tables": {
|
||||||
"app_metadata": {
|
"app_metadata": {
|
||||||
"name": "app_metadata",
|
"name": "app_metadata",
|
||||||
@@ -249,13 +249,6 @@
|
|||||||
"notNull": true,
|
"notNull": true,
|
||||||
"autoincrement": true
|
"autoincrement": true
|
||||||
},
|
},
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"volume_id": {
|
"volume_id": {
|
||||||
"name": "volume_id",
|
"name": "volume_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -353,15 +346,7 @@
|
|||||||
"default": "(unixepoch() * 1000)"
|
"default": "(unixepoch() * 1000)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {},
|
||||||
"backup_schedules_table_name_unique": {
|
|
||||||
"name": "backup_schedules_table_name_unique",
|
|
||||||
"columns": [
|
|
||||||
"name"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"backup_schedules_table_volume_id_volumes_table_id_fk": {
|
"backup_schedules_table_volume_id_volumes_table_id_fk": {
|
||||||
"name": "backup_schedules_table_volume_id_volumes_table_id_fk",
|
"name": "backup_schedules_table_volume_id_volumes_table_id_fk",
|
||||||
|
|||||||
@@ -131,22 +131,15 @@
|
|||||||
{
|
{
|
||||||
"idx": 18,
|
"idx": 18,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764794371040,
|
"when": 1764619898949,
|
||||||
"tag": "0018_breezy_invaders",
|
"tag": "0018_bizarre_zzzax",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 19,
|
"idx": 19,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764839917446,
|
"when": 1764790151212,
|
||||||
"tag": "0019_secret_nomad",
|
"tag": "0019_heavy_shen",
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 20,
|
|
||||||
"version": "6",
|
|
||||||
"when": 1764847918249,
|
|
||||||
"tag": "0020_even_dexter_bennett",
|
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { relations, sql } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
import { int, integer, sqliteTable, text, primaryKey, unique } from "drizzle-orm/sqlite-core";
|
import { int, integer, sqliteTable, text, primaryKey, uniqueIndex, unique } from "drizzle-orm/sqlite-core";
|
||||||
import type { CompressionMode, RepositoryBackend, repositoryConfigSchema, RepositoryStatus } from "~/schemas/restic";
|
import type { CompressionMode, RepositoryBackend, repositoryConfigSchema, RepositoryStatus } from "~/schemas/restic";
|
||||||
import type { BackendStatus, BackendType, volumeConfigSchema } from "~/schemas/volumes";
|
import type { BackendStatus, BackendType, volumeConfigSchema } from "~/schemas/volumes";
|
||||||
import type { NotificationType, notificationConfigSchema } from "~/schemas/notifications";
|
import type { NotificationType, notificationConfigSchema } from "~/schemas/notifications";
|
||||||
|
|||||||
@@ -281,12 +281,6 @@ const backup = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options?.excludeIfPresent && options.excludeIfPresent.length > 0) {
|
|
||||||
for (const filename of options.excludeIfPresent) {
|
|
||||||
args.push("--exclude-if-present", filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommonArgs(args, env);
|
addCommonArgs(args, env);
|
||||||
|
|
||||||
const logData = throttle((data: string) => {
|
const logData = throttle((data: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user