Compare commits
4 Commits
refactor/a
...
v0.14.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d9d3d5d3d | ||
|
|
8e90c4ace1 | ||
|
|
803eb1cd76 | ||
|
|
673827f9f3 |
@@ -709,7 +709,7 @@ export type ListRepositoriesResponses = {
|
|||||||
* List of repositories
|
* List of repositories
|
||||||
*/
|
*/
|
||||||
200: Array<{
|
200: Array<{
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
@@ -849,7 +849,7 @@ export type CreateRepositoryData = {
|
|||||||
isExistingRepository?: boolean;
|
isExistingRepository?: boolean;
|
||||||
};
|
};
|
||||||
name: string;
|
name: string;
|
||||||
compressionMode?: 'auto' | 'better' | 'fastest' | 'max' | 'off';
|
compressionMode?: 'auto' | 'max' | 'off';
|
||||||
};
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -924,7 +924,7 @@ export type GetRepositoryResponses = {
|
|||||||
* Repository details
|
* Repository details
|
||||||
*/
|
*/
|
||||||
200: {
|
200: {
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
@@ -1002,7 +1002,7 @@ export type GetRepositoryResponse = GetRepositoryResponses[keyof GetRepositoryRe
|
|||||||
|
|
||||||
export type UpdateRepositoryData = {
|
export type UpdateRepositoryData = {
|
||||||
body?: {
|
body?: {
|
||||||
compressionMode?: 'auto' | 'better' | 'fastest' | 'max' | 'off';
|
compressionMode?: 'auto' | 'max' | 'off';
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
@@ -1028,7 +1028,7 @@ export type UpdateRepositoryResponses = {
|
|||||||
* Repository updated successfully
|
* Repository updated successfully
|
||||||
*/
|
*/
|
||||||
200: {
|
200: {
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
@@ -1295,7 +1295,7 @@ export type ListBackupSchedulesResponses = {
|
|||||||
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
||||||
nextBackupAt: number | null;
|
nextBackupAt: number | null;
|
||||||
repository: {
|
repository: {
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
@@ -1528,7 +1528,7 @@ export type GetBackupScheduleResponses = {
|
|||||||
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
||||||
nextBackupAt: number | null;
|
nextBackupAt: number | null;
|
||||||
repository: {
|
repository: {
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
@@ -1742,7 +1742,7 @@ export type GetBackupScheduleForVolumeResponses = {
|
|||||||
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
lastBackupStatus: 'error' | 'in_progress' | 'success' | 'warning' | null;
|
||||||
nextBackupAt: number | null;
|
nextBackupAt: number | null;
|
||||||
repository: {
|
repository: {
|
||||||
compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null;
|
compressionMode: 'auto' | 'max' | 'off' | null;
|
||||||
config: {
|
config: {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
backend: 'r2';
|
backend: 'r2';
|
||||||
|
|||||||
@@ -176,10 +176,8 @@ export const CreateRepositoryForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="off">Off</SelectItem>
|
<SelectItem value="off">Off</SelectItem>
|
||||||
<SelectItem value="auto">Auto</SelectItem>
|
<SelectItem value="auto">Auto (fast)</SelectItem>
|
||||||
<SelectItem value="fastest">Fastest</SelectItem>
|
<SelectItem value="max">Max (slower, better compression)</SelectItem>
|
||||||
<SelectItem value="better">Better</SelectItem>
|
|
||||||
<SelectItem value="max">Max</SelectItem>
|
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormDescription>Compression mode for backups stored in this repository.</FormDescription>
|
<FormDescription>Compression mode for backups stored in this repository.</FormDescription>
|
||||||
|
|||||||
@@ -148,13 +148,13 @@ export const ScheduleSummary = (props: Props) => {
|
|||||||
<div>
|
<div>
|
||||||
<p className="text-xs uppercase text-muted-foreground">Last backup</p>
|
<p className="text-xs uppercase text-muted-foreground">Last backup</p>
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleString() : "Never"}
|
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleString() : "Never"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs uppercase text-muted-foreground">Next backup</p>
|
<p className="text-xs uppercase text-muted-foreground">Next backup</p>
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleString() : "Never"}
|
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleString() : "Never"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -97,13 +97,13 @@ export default function Backups({ loaderData }: Route.ComponentProps) {
|
|||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<span className="text-muted-foreground">Last backup</span>
|
<span className="text-muted-foreground">Last backup</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleDateString() : "Never"}
|
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleDateString() : "Never"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<span className="text-muted-foreground">Next backup</span>
|
<span className="text-muted-foreground">Next backup</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleDateString() : "N/A"}
|
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleDateString() : "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-muted-foreground">Created At</div>
|
<div className="text-sm font-medium text-muted-foreground">Created At</div>
|
||||||
<p className="mt-1 text-sm">{new Date(repository.createdAt * 1000).toLocaleString()}</p>
|
<p className="mt-1 text-sm">{new Date(repository.createdAt).toLocaleString()}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-muted-foreground">Last Checked</div>
|
<div className="text-sm font-medium text-muted-foreground">Last Checked</div>
|
||||||
<p className="mt-1 text-sm">
|
<p className="mt-1 text-sm">
|
||||||
{repository.lastChecked ? new Date(repository.lastChecked * 1000).toLocaleString() : "Never"}
|
{repository.lastChecked ? new Date(repository.lastChecked).toLocaleString() : "Never"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const HealthchecksCard = ({ volume }: Props) => {
|
export const HealthchecksCard = ({ volume }: Props) => {
|
||||||
const timeAgo = formatDistanceToNow(volume.lastHealthCheck * 1000, {
|
const timeAgo = formatDistanceToNow(volume.lastHealthCheck, {
|
||||||
addSuffix: true,
|
addSuffix: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
47
app/drizzle/0016_fix-timestamps-to-ms.sql
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
-- Convert timestamps from seconds to milliseconds (multiply by 1000)
|
||||||
|
-- Only convert values that appear to be in seconds (less than year 2100 threshold)
|
||||||
|
|
||||||
|
UPDATE `volumes_table` SET `last_health_check` = `last_health_check` * 1000 WHERE `last_health_check` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `volumes_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `volumes_table` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `users_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `users_table` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `sessions_table` SET `expires_at` = `expires_at` * 1000 WHERE `expires_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `sessions_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `repositories_table` SET `last_checked` = `last_checked` * 1000 WHERE `last_checked` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `repositories_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `repositories_table` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `backup_schedules_table` SET `last_backup_at` = `last_backup_at` * 1000 WHERE `last_backup_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `backup_schedules_table` SET `next_backup_at` = `next_backup_at` * 1000 WHERE `next_backup_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `backup_schedules_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `backup_schedules_table` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `notification_destinations_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `notification_destinations_table` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `backup_schedule_notifications_table` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
|
||||||
|
UPDATE `app_metadata` SET `created_at` = `created_at` * 1000 WHERE `created_at` < 4102444800;
|
||||||
|
--> statement-breakpoint
|
||||||
|
UPDATE `app_metadata` SET `updated_at` = `updated_at` * 1000 WHERE `updated_at` < 4102444800;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
-- Custom SQL migration file, put your code below! --
|
|
||||||
UPDATE `volumes_table` SET `last_health_check` = `last_health_check` / 1000 WHERE `last_health_check` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `volumes_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `volumes_table` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `users_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `users_table` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `sessions_table` SET `expires_at` = `expires_at` / 1000 WHERE `expires_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `sessions_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `repositories_table` SET `last_checked` = `last_checked` / 1000 WHERE `last_checked` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `repositories_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `repositories_table` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `backup_schedules_table` SET `last_backup_at` = `last_backup_at` / 1000 WHERE `last_backup_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `backup_schedules_table` SET `next_backup_at` = `next_backup_at` / 1000 WHERE `next_backup_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `backup_schedules_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `backup_schedules_table` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `notification_destinations_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `notification_destinations_table` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `backup_schedule_notifications_table` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
|
|
||||||
UPDATE `app_metadata` SET `created_at` = `created_at` / 1000 WHERE `created_at` > 4102444800;
|
|
||||||
--> statement-breakpoint
|
|
||||||
UPDATE `app_metadata` SET `updated_at` = `updated_at` / 1000 WHERE `updated_at` > 4102444800;
|
|
||||||
|
|
||||||
1
app/drizzle/0017_fix-compression-modes.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
UPDATE `repositories_table` SET `compression_mode` = 'auto' WHERE `compression_mode` IN ('fastest', 'better');
|
||||||
688
app/drizzle/meta/0017_snapshot.json
Normal file
@@ -0,0 +1,688 @@
|
|||||||
|
{
|
||||||
|
"id": "d0bfd316-b8f5-459b-ab17-0ce679479321",
|
||||||
|
"prevId": "e50ff0fb-4111-4d20-b550-9407ee397517",
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"tables": {
|
||||||
|
"app_metadata": {
|
||||||
|
"name": "app_metadata",
|
||||||
|
"columns": {
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"backup_schedule_notifications_table": {
|
||||||
|
"name": "backup_schedule_notifications_table",
|
||||||
|
"columns": {
|
||||||
|
"schedule_id": {
|
||||||
|
"name": "schedule_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"destination_id": {
|
||||||
|
"name": "destination_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"notify_on_start": {
|
||||||
|
"name": "notify_on_start",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"notify_on_success": {
|
||||||
|
"name": "notify_on_success",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"notify_on_failure": {
|
||||||
|
"name": "notify_on_failure",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk": {
|
||||||
|
"name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
|
||||||
|
"tableFrom": "backup_schedule_notifications_table",
|
||||||
|
"columnsFrom": [
|
||||||
|
"schedule_id"
|
||||||
|
],
|
||||||
|
"tableTo": "backup_schedules_table",
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onUpdate": "no action",
|
||||||
|
"onDelete": "cascade"
|
||||||
|
},
|
||||||
|
"backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk": {
|
||||||
|
"name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
|
||||||
|
"tableFrom": "backup_schedule_notifications_table",
|
||||||
|
"columnsFrom": [
|
||||||
|
"destination_id"
|
||||||
|
],
|
||||||
|
"tableTo": "notification_destinations_table",
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onUpdate": "no action",
|
||||||
|
"onDelete": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"backup_schedule_notifications_table_schedule_id_destination_id_pk": {
|
||||||
|
"columns": [
|
||||||
|
"schedule_id",
|
||||||
|
"destination_id"
|
||||||
|
],
|
||||||
|
"name": "backup_schedule_notifications_table_schedule_id_destination_id_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"backup_schedules_table": {
|
||||||
|
"name": "backup_schedules_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"volume_id": {
|
||||||
|
"name": "volume_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"repository_id": {
|
||||||
|
"name": "repository_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"cron_expression": {
|
||||||
|
"name": "cron_expression",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"retention_policy": {
|
||||||
|
"name": "retention_policy",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"exclude_patterns": {
|
||||||
|
"name": "exclude_patterns",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'[]'"
|
||||||
|
},
|
||||||
|
"include_patterns": {
|
||||||
|
"name": "include_patterns",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'[]'"
|
||||||
|
},
|
||||||
|
"last_backup_at": {
|
||||||
|
"name": "last_backup_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"last_backup_status": {
|
||||||
|
"name": "last_backup_status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"last_backup_error": {
|
||||||
|
"name": "last_backup_error",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"next_backup_at": {
|
||||||
|
"name": "next_backup_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"backup_schedules_table_volume_id_volumes_table_id_fk": {
|
||||||
|
"name": "backup_schedules_table_volume_id_volumes_table_id_fk",
|
||||||
|
"tableFrom": "backup_schedules_table",
|
||||||
|
"columnsFrom": [
|
||||||
|
"volume_id"
|
||||||
|
],
|
||||||
|
"tableTo": "volumes_table",
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onUpdate": "no action",
|
||||||
|
"onDelete": "cascade"
|
||||||
|
},
|
||||||
|
"backup_schedules_table_repository_id_repositories_table_id_fk": {
|
||||||
|
"name": "backup_schedules_table_repository_id_repositories_table_id_fk",
|
||||||
|
"tableFrom": "backup_schedules_table",
|
||||||
|
"columnsFrom": [
|
||||||
|
"repository_id"
|
||||||
|
],
|
||||||
|
"tableTo": "repositories_table",
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onUpdate": "no action",
|
||||||
|
"onDelete": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"notification_destinations_table": {
|
||||||
|
"name": "notification_destinations_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"name": "config",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"notification_destinations_table_name_unique": {
|
||||||
|
"name": "notification_destinations_table_name_unique",
|
||||||
|
"columns": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"repositories_table": {
|
||||||
|
"name": "repositories_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"short_id": {
|
||||||
|
"name": "short_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"name": "config",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"compression_mode": {
|
||||||
|
"name": "compression_mode",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'auto'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'unknown'"
|
||||||
|
},
|
||||||
|
"last_checked": {
|
||||||
|
"name": "last_checked",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"last_error": {
|
||||||
|
"name": "last_error",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"repositories_table_short_id_unique": {
|
||||||
|
"name": "repositories_table_short_id_unique",
|
||||||
|
"columns": [
|
||||||
|
"short_id"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"repositories_table_name_unique": {
|
||||||
|
"name": "repositories_table_name_unique",
|
||||||
|
"columns": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"sessions_table": {
|
||||||
|
"name": "sessions_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"expires_at": {
|
||||||
|
"name": "expires_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"sessions_table_user_id_users_table_id_fk": {
|
||||||
|
"name": "sessions_table_user_id_users_table_id_fk",
|
||||||
|
"tableFrom": "sessions_table",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"tableTo": "users_table",
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onUpdate": "no action",
|
||||||
|
"onDelete": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"users_table": {
|
||||||
|
"name": "users_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"password_hash": {
|
||||||
|
"name": "password_hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"has_downloaded_restic_password": {
|
||||||
|
"name": "has_downloaded_restic_password",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"users_table_username_unique": {
|
||||||
|
"name": "users_table_username_unique",
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"volumes_table": {
|
||||||
|
"name": "volumes_table",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"short_id": {
|
||||||
|
"name": "short_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'unmounted'"
|
||||||
|
},
|
||||||
|
"last_error": {
|
||||||
|
"name": "last_error",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"last_health_check": {
|
||||||
|
"name": "last_health_check",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(unixepoch())"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"name": "config",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"auto_remount": {
|
||||||
|
"name": "auto_remount",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"volumes_table_short_id_unique": {
|
||||||
|
"name": "volumes_table_short_id_unique",
|
||||||
|
"columns": [
|
||||||
|
"short_id"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"volumes_table_name_unique": {
|
||||||
|
"name": "volumes_table_name_unique",
|
||||||
|
"columns": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,125 +1,132 @@
|
|||||||
{
|
{
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1755765658194,
|
"when": 1755765658194,
|
||||||
"tag": "0000_known_madelyne_pryor",
|
"tag": "0000_known_madelyne_pryor",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1755775437391,
|
"when": 1755775437391,
|
||||||
"tag": "0001_far_frank_castle",
|
"tag": "0001_far_frank_castle",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1756930554198,
|
"when": 1756930554198,
|
||||||
"tag": "0002_cheerful_randall",
|
"tag": "0002_cheerful_randall",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 3,
|
"idx": 3,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1758653407064,
|
"when": 1758653407064,
|
||||||
"tag": "0003_mature_hellcat",
|
"tag": "0003_mature_hellcat",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 4,
|
"idx": 4,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1758961535488,
|
"when": 1758961535488,
|
||||||
"tag": "0004_wealthy_tomas",
|
"tag": "0004_wealthy_tomas",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 5,
|
"idx": 5,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1759416698274,
|
"when": 1759416698274,
|
||||||
"tag": "0005_simple_alice",
|
"tag": "0005_simple_alice",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 6,
|
"idx": 6,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1760734377440,
|
"when": 1760734377440,
|
||||||
"tag": "0006_secret_micromacro",
|
"tag": "0006_secret_micromacro",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 7,
|
"idx": 7,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1761224911352,
|
"when": 1761224911352,
|
||||||
"tag": "0007_watery_sersi",
|
"tag": "0007_watery_sersi",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 8,
|
"idx": 8,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1761414054481,
|
"when": 1761414054481,
|
||||||
"tag": "0008_silent_lady_bullseye",
|
"tag": "0008_silent_lady_bullseye",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 9,
|
"idx": 9,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1762095226041,
|
"when": 1762095226041,
|
||||||
"tag": "0009_little_adam_warlock",
|
"tag": "0009_little_adam_warlock",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 10,
|
"idx": 10,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1762610065889,
|
"when": 1762610065889,
|
||||||
"tag": "0010_perfect_proemial_gods",
|
"tag": "0010_perfect_proemial_gods",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 11,
|
"idx": 11,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1763644043601,
|
"when": 1763644043601,
|
||||||
"tag": "0011_familiar_stone_men",
|
"tag": "0011_familiar_stone_men",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 12,
|
"idx": 12,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764100562084,
|
"when": 1764100562084,
|
||||||
"tag": "0012_add_short_ids",
|
"tag": "0012_add_short_ids",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 13,
|
"idx": 13,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764182159797,
|
"when": 1764182159797,
|
||||||
"tag": "0013_elite_sprite",
|
"tag": "0013_elite_sprite",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 14,
|
"idx": 14,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764182405089,
|
"when": 1764182405089,
|
||||||
"tag": "0014_wild_echo",
|
"tag": "0014_wild_echo",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 15,
|
"idx": 15,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764182465287,
|
"when": 1764182465287,
|
||||||
"tag": "0015_jazzy_sersi",
|
"tag": "0015_jazzy_sersi",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 16,
|
"idx": 16,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1764193182689,
|
"when": 1764194697035,
|
||||||
"tag": "0016_fix-timestamps-to-seconds",
|
"tag": "0016_fix-timestamps-to-ms",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
}
|
"idx": 17,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1764357897219,
|
||||||
|
"tag": "0017_fix-compression-modes",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -93,8 +93,6 @@ export type RepositoryConfig = typeof repositoryConfigSchema.infer;
|
|||||||
export const COMPRESSION_MODES = {
|
export const COMPRESSION_MODES = {
|
||||||
off: "off",
|
off: "off",
|
||||||
auto: "auto",
|
auto: "auto",
|
||||||
fastest: "fastest",
|
|
||||||
better: "better",
|
|
||||||
max: "max",
|
max: "max",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ export const volumesTable = sqliteTable("volumes_table", {
|
|||||||
type: text().$type<BackendType>().notNull(),
|
type: text().$type<BackendType>().notNull(),
|
||||||
status: text().$type<BackendStatus>().notNull().default("unmounted"),
|
status: text().$type<BackendStatus>().notNull().default("unmounted"),
|
||||||
lastError: text("last_error"),
|
lastError: text("last_error"),
|
||||||
lastHealthCheck: integer("last_health_check", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
lastHealthCheck: integer("last_health_check", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
createdAt: integer("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: integer("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: integer("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: integer("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
|
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
|
||||||
autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true),
|
autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true),
|
||||||
});
|
});
|
||||||
@@ -30,8 +30,8 @@ export const usersTable = sqliteTable("users_table", {
|
|||||||
username: text().notNull().unique(),
|
username: text().notNull().unique(),
|
||||||
passwordHash: text("password_hash").notNull(),
|
passwordHash: text("password_hash").notNull(),
|
||||||
hasDownloadedResticPassword: int("has_downloaded_restic_password", { mode: "boolean" }).notNull().default(false),
|
hasDownloadedResticPassword: int("has_downloaded_restic_password", { mode: "boolean" }).notNull().default(false),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export type User = typeof usersTable.$inferSelect;
|
export type User = typeof usersTable.$inferSelect;
|
||||||
export const sessionsTable = sqliteTable("sessions_table", {
|
export const sessionsTable = sqliteTable("sessions_table", {
|
||||||
@@ -40,7 +40,7 @@ export const sessionsTable = sqliteTable("sessions_table", {
|
|||||||
.notNull()
|
.notNull()
|
||||||
.references(() => usersTable.id, { onDelete: "cascade" }),
|
.references(() => usersTable.id, { onDelete: "cascade" }),
|
||||||
expiresAt: int("expires_at", { mode: "number" }).notNull(),
|
expiresAt: int("expires_at", { mode: "number" }).notNull(),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export type Session = typeof sessionsTable.$inferSelect;
|
export type Session = typeof sessionsTable.$inferSelect;
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ export const repositoriesTable = sqliteTable("repositories_table", {
|
|||||||
status: text().$type<RepositoryStatus>().default("unknown"),
|
status: text().$type<RepositoryStatus>().default("unknown"),
|
||||||
lastChecked: int("last_checked", { mode: "number" }),
|
lastChecked: int("last_checked", { mode: "number" }),
|
||||||
lastError: text("last_error"),
|
lastError: text("last_error"),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export type Repository = typeof repositoriesTable.$inferSelect;
|
export type Repository = typeof repositoriesTable.$inferSelect;
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ export const backupSchedulesTable = sqliteTable("backup_schedules_table", {
|
|||||||
lastBackupStatus: text("last_backup_status").$type<"success" | "error" | "in_progress" | "warning">(),
|
lastBackupStatus: text("last_backup_status").$type<"success" | "error" | "in_progress" | "warning">(),
|
||||||
lastBackupError: text("last_backup_error"),
|
lastBackupError: text("last_backup_error"),
|
||||||
nextBackupAt: int("next_backup_at", { mode: "number" }),
|
nextBackupAt: int("next_backup_at", { mode: "number" }),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export const backupScheduleRelations = relations(backupSchedulesTable, ({ one, many }) => ({
|
export const backupScheduleRelations = relations(backupSchedulesTable, ({ one, many }) => ({
|
||||||
volume: one(volumesTable, {
|
volume: one(volumesTable, {
|
||||||
@@ -115,8 +115,8 @@ export const notificationDestinationsTable = sqliteTable("notification_destinati
|
|||||||
enabled: int("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: int("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
type: text().$type<NotificationType>().notNull(),
|
type: text().$type<NotificationType>().notNull(),
|
||||||
config: text("config", { mode: "json" }).$type<typeof notificationConfigSchema.inferOut>().notNull(),
|
config: text("config", { mode: "json" }).$type<typeof notificationConfigSchema.inferOut>().notNull(),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export const notificationDestinationRelations = relations(notificationDestinationsTable, ({ many }) => ({
|
export const notificationDestinationRelations = relations(notificationDestinationsTable, ({ many }) => ({
|
||||||
schedules: many(backupScheduleNotificationsTable),
|
schedules: many(backupScheduleNotificationsTable),
|
||||||
@@ -138,7 +138,7 @@ export const backupScheduleNotificationsTable = sqliteTable(
|
|||||||
notifyOnStart: int("notify_on_start", { mode: "boolean" }).notNull().default(false),
|
notifyOnStart: int("notify_on_start", { mode: "boolean" }).notNull().default(false),
|
||||||
notifyOnSuccess: int("notify_on_success", { mode: "boolean" }).notNull().default(false),
|
notifyOnSuccess: int("notify_on_success", { mode: "boolean" }).notNull().default(false),
|
||||||
notifyOnFailure: int("notify_on_failure", { mode: "boolean" }).notNull().default(true),
|
notifyOnFailure: int("notify_on_failure", { mode: "boolean" }).notNull().default(true),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
},
|
},
|
||||||
(table) => [primaryKey({ columns: [table.scheduleId, table.destinationId] })],
|
(table) => [primaryKey({ columns: [table.scheduleId, table.destinationId] })],
|
||||||
);
|
);
|
||||||
@@ -161,7 +161,7 @@ export type BackupScheduleNotification = typeof backupScheduleNotificationsTable
|
|||||||
export const appMetadataTable = sqliteTable("app_metadata", {
|
export const appMetadataTable = sqliteTable("app_metadata", {
|
||||||
key: text().primaryKey(),
|
key: text().primaryKey(),
|
||||||
value: text().notNull(),
|
value: text().notNull(),
|
||||||
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
createdAt: int("created_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch())`),
|
updatedAt: int("updated_at", { mode: "number" }).notNull().default(sql`(unixepoch() * 1000)`),
|
||||||
});
|
});
|
||||||
export type AppMetadata = typeof appMetadataTable.$inferSelect;
|
export type AppMetadata = typeof appMetadataTable.$inferSelect;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const authController = new Hono()
|
|||||||
|
|
||||||
setCookie(c, COOKIE_NAME, sessionId, {
|
setCookie(c, COOKIE_NAME, sessionId, {
|
||||||
...COOKIE_OPTIONS,
|
...COOKIE_OPTIONS,
|
||||||
expires: new Date(expiresAt * 1000),
|
expires: new Date(expiresAt),
|
||||||
});
|
});
|
||||||
|
|
||||||
return c.json<LoginDto>({
|
return c.json<LoginDto>({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { db } from "../../db/db";
|
|||||||
import { sessionsTable, usersTable } from "../../db/schema";
|
import { sessionsTable, usersTable } from "../../db/schema";
|
||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
|
|
||||||
const SESSION_DURATION = 60 * 60 * 24 * 30; // 30 days in seconds
|
const SESSION_DURATION = 60 * 60 * 24 * 30 * 1000; // 30 days in milliseconds
|
||||||
|
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +30,7 @@ export class AuthService {
|
|||||||
|
|
||||||
logger.info(`User registered: ${username}`);
|
logger.info(`User registered: ${username}`);
|
||||||
const sessionId = crypto.randomUUID();
|
const sessionId = crypto.randomUUID();
|
||||||
const expiresAt = Math.floor(Date.now() / 1000) + SESSION_DURATION;
|
const expiresAt = Date.now() + SESSION_DURATION;
|
||||||
|
|
||||||
await db.insert(sessionsTable).values({
|
await db.insert(sessionsTable).values({
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
@@ -66,7 +66,7 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionId = crypto.randomUUID();
|
const sessionId = crypto.randomUUID();
|
||||||
const expiresAt = Math.floor(Date.now() / 1000) + SESSION_DURATION;
|
const expiresAt = Date.now() + SESSION_DURATION;
|
||||||
|
|
||||||
await db.insert(sessionsTable).values({
|
await db.insert(sessionsTable).values({
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
@@ -112,7 +112,7 @@ export class AuthService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.session.expiresAt < Math.floor(Date.now() / 1000)) {
|
if (session.session.expiresAt < Date.now()) {
|
||||||
await db.delete(sessionsTable).where(eq(sessionsTable.id, sessionId));
|
await db.delete(sessionsTable).where(eq(sessionsTable.id, sessionId));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -134,10 +134,7 @@ export class AuthService {
|
|||||||
* Clean up expired sessions
|
* Clean up expired sessions
|
||||||
*/
|
*/
|
||||||
async cleanupExpiredSessions() {
|
async cleanupExpiredSessions() {
|
||||||
const result = await db
|
const result = await db.delete(sessionsTable).where(lt(sessionsTable.expiresAt, Date.now())).returning();
|
||||||
.delete(sessionsTable)
|
|
||||||
.where(lt(sessionsTable.expiresAt, Math.floor(Date.now() / 1000)))
|
|
||||||
.returning();
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
logger.info(`Cleaned up ${result.length} expired sessions`);
|
logger.info(`Cleaned up ${result.length} expired sessions`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ const calculateNextRun = (cronExpression: string): number => {
|
|||||||
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Math.floor(interval.next().getTime() / 1000);
|
return interval.next().getTime();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to parse cron expression "${cronExpression}": ${error}`);
|
logger.error(`Failed to parse cron expression "${cronExpression}": ${error}`);
|
||||||
const fallback = new Date();
|
const fallback = new Date();
|
||||||
fallback.setMinutes(fallback.getMinutes() + 1);
|
fallback.setMinutes(fallback.getMinutes() + 1);
|
||||||
return Math.floor(fallback.getTime() / 1000);
|
return fallback.getTime();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
|
|||||||
|
|
||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(backupSchedulesTable)
|
.update(backupSchedulesTable)
|
||||||
.set({ ...data, nextBackupAt, updatedAt: Math.floor(Date.now() / 1000) })
|
.set({ ...data, nextBackupAt, updatedAt: Date.now() })
|
||||||
.where(eq(backupSchedulesTable.id, scheduleId))
|
.where(eq(backupSchedulesTable.id, scheduleId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||||||
.update(backupSchedulesTable)
|
.update(backupSchedulesTable)
|
||||||
.set({
|
.set({
|
||||||
lastBackupStatus: "in_progress",
|
lastBackupStatus: "in_progress",
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
lastBackupError: null,
|
lastBackupError: null,
|
||||||
nextBackupAt,
|
nextBackupAt,
|
||||||
})
|
})
|
||||||
@@ -262,11 +262,11 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||||||
await db
|
await db
|
||||||
.update(backupSchedulesTable)
|
.update(backupSchedulesTable)
|
||||||
.set({
|
.set({
|
||||||
lastBackupAt: Math.floor(Date.now() / 1000),
|
lastBackupAt: Date.now(),
|
||||||
lastBackupStatus: exitCode === 0 ? "success" : "warning",
|
lastBackupStatus: exitCode === 0 ? "success" : "warning",
|
||||||
lastBackupError: null,
|
lastBackupError: null,
|
||||||
nextBackupAt: nextBackupAt,
|
nextBackupAt: nextBackupAt,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(backupSchedulesTable.id, scheduleId));
|
.where(eq(backupSchedulesTable.id, scheduleId));
|
||||||
|
|
||||||
@@ -297,10 +297,10 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||||||
await db
|
await db
|
||||||
.update(backupSchedulesTable)
|
.update(backupSchedulesTable)
|
||||||
.set({
|
.set({
|
||||||
lastBackupAt: Math.floor(Date.now() / 1000),
|
lastBackupAt: Date.now(),
|
||||||
lastBackupStatus: "error",
|
lastBackupStatus: "error",
|
||||||
lastBackupError: toMessage(error),
|
lastBackupError: toMessage(error),
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(backupSchedulesTable.id, scheduleId));
|
.where(eq(backupSchedulesTable.id, scheduleId));
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getSchedulesToExecute = async () => {
|
const getSchedulesToExecute = async () => {
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Date.now();
|
||||||
const schedules = await db.query.backupSchedulesTable.findMany({
|
const schedules = await db.query.backupSchedulesTable.findMany({
|
||||||
where: eq(backupSchedulesTable.enabled, true),
|
where: eq(backupSchedulesTable.enabled, true),
|
||||||
});
|
});
|
||||||
@@ -367,7 +367,7 @@ const stopBackup = async (scheduleId: number) => {
|
|||||||
.set({
|
.set({
|
||||||
lastBackupStatus: "error",
|
lastBackupStatus: "error",
|
||||||
lastBackupError: "Backup was stopped by user",
|
lastBackupError: "Backup was stopped by user",
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(backupSchedulesTable.id, scheduleId));
|
.where(eq(backupSchedulesTable.id, scheduleId));
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const MIGRATION_KEY_PREFIX = "migration:";
|
|||||||
|
|
||||||
export const recordMigrationCheckpoint = async (version: string): Promise<void> => {
|
export const recordMigrationCheckpoint = async (version: string): Promise<void> => {
|
||||||
const key = `${MIGRATION_KEY_PREFIX}${version}`;
|
const key = `${MIGRATION_KEY_PREFIX}${version}`;
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Date.now();
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.insert(appMetadataTable)
|
.insert(appMetadataTable)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const migrateRepositoryFolders = async (): Promise<MigrationResult> => {
|
|||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
config: updatedConfig,
|
config: updatedConfig,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repo.id));
|
.where(eq(repositoriesTable.id, repo.id));
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ const migrateRepositoryFolders = async (): Promise<MigrationResult> => {
|
|||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
config: updatedConfig,
|
config: updatedConfig,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repo.id));
|
.where(eq(repositoriesTable.id, repo.id));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -175,7 +175,7 @@ const migrateRepositoryFolders = async (): Promise<MigrationResult> => {
|
|||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
config: updatedConfig,
|
config: updatedConfig,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repo.id));
|
.where(eq(repositoriesTable.id, repo.id));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ const updateDestination = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateData: Partial<NotificationDestination> = {
|
const updateData: Partial<NotificationDestination> = {
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (updates.name !== undefined) {
|
if (updates.name !== undefined) {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const createRepository = async (name: string, config: RepositoryConfig, compress
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
await db
|
await db
|
||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({ status: "healthy", lastChecked: Math.floor(Date.now() / 1000), lastError: null })
|
.set({ status: "healthy", lastChecked: Date.now(), lastError: null })
|
||||||
.where(eq(repositoriesTable.id, id));
|
.where(eq(repositoriesTable.id, id));
|
||||||
|
|
||||||
return { repository: created, status: 201 };
|
return { repository: created, status: 201 };
|
||||||
@@ -249,21 +249,18 @@ const checkHealth = async (repositoryId: string) => {
|
|||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error, status } = await restic
|
const { hasErrors, error } = await restic.check(repository.config, { readData: true });
|
||||||
.snapshots(repository.config)
|
|
||||||
.then(() => ({ error: null, status: "healthy" as const }))
|
|
||||||
.catch((error) => ({ error: toMessage(error), status: "error" as const }));
|
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
status,
|
status: hasErrors ? "error" : "healthy",
|
||||||
lastChecked: Math.floor(Date.now() / 1000),
|
lastChecked: Date.now(),
|
||||||
lastError: error,
|
lastError: error,
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repository.id));
|
.where(eq(repositoriesTable.id, repository.id));
|
||||||
|
|
||||||
return { status, lastError: error };
|
return { lastError: error };
|
||||||
};
|
};
|
||||||
|
|
||||||
const doctorRepository = async (name: string) => {
|
const doctorRepository = async (name: string) => {
|
||||||
@@ -335,7 +332,7 @@ const doctorRepository = async (name: string) => {
|
|||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
status: allSuccessful ? "healthy" : "error",
|
status: allSuccessful ? "healthy" : "error",
|
||||||
lastChecked: Math.floor(Date.now() / 1000),
|
lastChecked: Date.now(),
|
||||||
lastError: allSuccessful ? null : steps.find((s) => !s.success)?.error,
|
lastError: allSuccessful ? null : steps.find((s) => !s.success)?.error,
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repository.id));
|
.where(eq(repositoriesTable.id, repository.id));
|
||||||
@@ -396,7 +393,7 @@ const updateRepository = async (name: string, updates: { name?: string; compress
|
|||||||
.set({
|
.set({
|
||||||
name: newName,
|
name: newName,
|
||||||
compressionMode: updates.compressionMode ?? existing.compressionMode,
|
compressionMode: updates.compressionMode ?? existing.compressionMode,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Date.now(),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, existing.id))
|
.where(eq(repositoriesTable.id, existing.id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
|
|||||||
|
|
||||||
await db
|
await db
|
||||||
.update(volumesTable)
|
.update(volumesTable)
|
||||||
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
|
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
|
||||||
.where(eq(volumesTable.name, slug));
|
.where(eq(volumesTable.name, slug));
|
||||||
|
|
||||||
return { volume: created, status: 201 };
|
return { volume: created, status: 201 };
|
||||||
@@ -91,7 +91,7 @@ const mountVolume = async (name: string) => {
|
|||||||
|
|
||||||
await db
|
await db
|
||||||
.update(volumesTable)
|
.update(volumesTable)
|
||||||
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
|
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
|
||||||
.where(eq(volumesTable.name, name));
|
.where(eq(volumesTable.name, name));
|
||||||
|
|
||||||
if (status === "mounted") {
|
if (status === "mounted") {
|
||||||
@@ -196,7 +196,7 @@ const updateVolume = async (name: string, volumeData: UpdateVolumeBody) => {
|
|||||||
const { error, status } = await backend.mount();
|
const { error, status } = await backend.mount();
|
||||||
await db
|
await db
|
||||||
.update(volumesTable)
|
.update(volumesTable)
|
||||||
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
|
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
|
||||||
.where(eq(volumesTable.id, existing.id));
|
.where(eq(volumesTable.id, existing.id));
|
||||||
|
|
||||||
serverEvents.emit("volume:updated", { volumeName: updated.name });
|
serverEvents.emit("volume:updated", { volumeName: updated.name });
|
||||||
@@ -255,7 +255,7 @@ const checkHealth = async (name: string) => {
|
|||||||
|
|
||||||
await db
|
await db
|
||||||
.update(volumesTable)
|
.update(volumesTable)
|
||||||
.set({ lastHealthCheck: Math.floor(Date.now() / 1000), status, lastError: error ?? null })
|
.set({ lastHealthCheck: Date.now(), status, lastError: error ?? null })
|
||||||
.where(eq(volumesTable.name, volume.name));
|
.where(eq(volumesTable.name, volume.name));
|
||||||
|
|
||||||
return { status, error };
|
return { status, error };
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "Zerobyte",
|
"name": "Zerobyte",
|
||||||
"short_name": "Zerobyte",
|
"short_name": "Zerobyte",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/images/favicon/web-app-manifest-192x192.png",
|
"src": "/images/favicon/web-app-manifest-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/favicon/web-app-manifest-512x512.png",
|
"src": "/images/favicon/web-app-manifest-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#1b1b1b",
|
"theme_color": "#1b1b1b",
|
||||||
"background_color": "#1b1b1b",
|
"background_color": "#1b1b1b",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 28 KiB |