Compare commits

...

2 Commits

Author SHA1 Message Date
Nicolas Meienberger
150b4f6e89 chore: format files 2025-11-26 22:52:05 +01:00
Nicolas Meienberger
60f37076a8 refactor: change all timestamps to be in seconds 2025-11-26 22:50:52 +01:00
21 changed files with 3372 additions and 2795 deletions

View File

@@ -237,8 +237,7 @@ export const CreateRepositoryForm = ({
</SelectContent>
</Select>
<FormDescription>
Choose whether to use Zerobyte's master password or enter a custom password for the existing
repository.
Choose whether to use Zerobyte's master password or enter a custom password for the existing repository.
</FormDescription>
</FormItem>

View File

@@ -148,13 +148,13 @@ export const ScheduleSummary = (props: Props) => {
<div>
<p className="text-xs uppercase text-muted-foreground">Last backup</p>
<p className="font-medium">
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleString() : "Never"}
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleString() : "Never"}
</p>
</div>
<div>
<p className="text-xs uppercase text-muted-foreground">Next backup</p>
<p className="font-medium">
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleString() : "Never"}
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleString() : "Never"}
</p>
</div>

View File

@@ -67,7 +67,7 @@ export const SnapshotFileBrowser = (props: Props) => {
const addBasePath = useCallback(
(displayPath: string): string => {
let vbp = volumeBasePath === "/" ? "" : volumeBasePath;
const vbp = volumeBasePath === "/" ? "" : volumeBasePath;
if (!vbp) return displayPath;
if (displayPath === "/") return vbp;

View File

@@ -97,13 +97,13 @@ export default function Backups({ loaderData }: Route.ComponentProps) {
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Last backup</span>
<span className="font-medium">
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleDateString() : "Never"}
{schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleDateString() : "Never"}
</span>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Next backup</span>
<span className="font-medium">
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleDateString() : "N/A"}
{schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleDateString() : "N/A"}
</span>
</div>
</div>

View File

@@ -158,7 +158,10 @@ export default function Notifications({ loaderData }: Route.ComponentProps) {
<TableCell className="font-medium text-strong-accent">{notification.name}</TableCell>
<TableCell className="capitalize">{notification.type}</TableCell>
<TableCell className="text-center">
<StatusDot variant={notification.enabled ? "success" : "neutral"} label={notification.enabled ? "Enabled" : "Disabled"} />
<StatusDot
variant={notification.enabled ? "success" : "neutral"}
label={notification.enabled ? "Enabled" : "Disabled"}
/>
</TableCell>
</TableRow>
))

View File

@@ -107,10 +107,7 @@ export const RestoreSnapshotForm = ({ formId, onSubmit, className }: Props) => {
className="h-auto p-0 text-sm font-normal"
>
Advanced
<ChevronDown
size={16}
className={`ml-1 transition-transform ${showAdvanced ? "rotate-180" : ""}`}
/>
<ChevronDown size={16} className={`ml-1 transition-transform ${showAdvanced ? "rotate-180" : ""}`} />
</Button>
{showAdvanced && (

View File

@@ -35,7 +35,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
<div>
<div className="text-sm font-medium text-muted-foreground">Last Checked</div>
<p className="mt-1 text-sm">
{repository.lastChecked ? new Date(repository.lastChecked).toLocaleString() : "Never"}
{repository.lastChecked ? new Date(repository.lastChecked * 1000).toLocaleString() : "Never"}
</p>
</div>
</div>

View File

@@ -13,7 +13,7 @@ type Props = {
};
export const HealthchecksCard = ({ volume }: Props) => {
const timeAgo = formatDistanceToNow(volume.lastHealthCheck, {
const timeAgo = formatDistanceToNow(volume.lastHealthCheck * 1000, {
addSuffix: true,
});

View File

@@ -0,0 +1,46 @@
-- 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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -651,4 +651,3 @@
"indexes": {}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,653 @@
{
"id": "9cfb3302-0207-4eb1-89ae-d65078ceb0ac",
"prevId": "e52fe10a-3f36-4b21-abef-c15990d28363",
"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": {}
}
}

View File

@@ -1,118 +1,125 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1755765658194,
"tag": "0000_known_madelyne_pryor",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1755775437391,
"tag": "0001_far_frank_castle",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1756930554198,
"tag": "0002_cheerful_randall",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1758653407064,
"tag": "0003_mature_hellcat",
"breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1758961535488,
"tag": "0004_wealthy_tomas",
"breakpoints": true
},
{
"idx": 5,
"version": "6",
"when": 1759416698274,
"tag": "0005_simple_alice",
"breakpoints": true
},
{
"idx": 6,
"version": "6",
"when": 1760734377440,
"tag": "0006_secret_micromacro",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1761224911352,
"tag": "0007_watery_sersi",
"breakpoints": true
},
{
"idx": 8,
"version": "6",
"when": 1761414054481,
"tag": "0008_silent_lady_bullseye",
"breakpoints": true
},
{
"idx": 9,
"version": "6",
"when": 1762095226041,
"tag": "0009_little_adam_warlock",
"breakpoints": true
},
{
"idx": 10,
"version": "6",
"when": 1762610065889,
"tag": "0010_perfect_proemial_gods",
"breakpoints": true
},
{
"idx": 11,
"version": "6",
"when": 1763644043601,
"tag": "0011_familiar_stone_men",
"breakpoints": true
},
{
"idx": 12,
"version": "6",
"when": 1764100562084,
"tag": "0012_add_short_ids",
"breakpoints": true
},
{
"idx": 13,
"version": "6",
"when": 1764182159797,
"tag": "0013_elite_sprite",
"breakpoints": true
},
{
"idx": 14,
"version": "6",
"when": 1764182405089,
"tag": "0014_wild_echo",
"breakpoints": true
},
{
"idx": 15,
"version": "6",
"when": 1764182465287,
"tag": "0015_jazzy_sersi",
"breakpoints": true
}
]
}
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1755765658194,
"tag": "0000_known_madelyne_pryor",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1755775437391,
"tag": "0001_far_frank_castle",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1756930554198,
"tag": "0002_cheerful_randall",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1758653407064,
"tag": "0003_mature_hellcat",
"breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1758961535488,
"tag": "0004_wealthy_tomas",
"breakpoints": true
},
{
"idx": 5,
"version": "6",
"when": 1759416698274,
"tag": "0005_simple_alice",
"breakpoints": true
},
{
"idx": 6,
"version": "6",
"when": 1760734377440,
"tag": "0006_secret_micromacro",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1761224911352,
"tag": "0007_watery_sersi",
"breakpoints": true
},
{
"idx": 8,
"version": "6",
"when": 1761414054481,
"tag": "0008_silent_lady_bullseye",
"breakpoints": true
},
{
"idx": 9,
"version": "6",
"when": 1762095226041,
"tag": "0009_little_adam_warlock",
"breakpoints": true
},
{
"idx": 10,
"version": "6",
"when": 1762610065889,
"tag": "0010_perfect_proemial_gods",
"breakpoints": true
},
{
"idx": 11,
"version": "6",
"when": 1763644043601,
"tag": "0011_familiar_stone_men",
"breakpoints": true
},
{
"idx": 12,
"version": "6",
"when": 1764100562084,
"tag": "0012_add_short_ids",
"breakpoints": true
},
{
"idx": 13,
"version": "6",
"when": 1764182159797,
"tag": "0013_elite_sprite",
"breakpoints": true
},
{
"idx": 14,
"version": "6",
"when": 1764182405089,
"tag": "0014_wild_echo",
"breakpoints": true
},
{
"idx": 15,
"version": "6",
"when": 1764182465287,
"tag": "0015_jazzy_sersi",
"breakpoints": true
},
{
"idx": 16,
"version": "6",
"when": 1764193182689,
"tag": "0016_fix-timestamps-to-seconds",
"breakpoints": true
}
]
}

View File

@@ -21,6 +21,7 @@ import {
} from "./auth.dto";
import { authService } from "./auth.service";
import { toMessage } from "../../utils/errors";
import { logger } from "~/server/utils/logger";
const COOKIE_NAME = "session_id";
const COOKIE_OPTIONS = {
@@ -66,7 +67,7 @@ export const authController = new Hono()
setCookie(c, COOKIE_NAME, sessionId, {
...COOKIE_OPTIONS,
expires: new Date(expiresAt),
expires: new Date(expiresAt * 1000),
});
return c.json<LoginDto>({

View File

@@ -3,7 +3,7 @@ import { db } from "../../db/db";
import { sessionsTable, usersTable } from "../../db/schema";
import { logger } from "../../utils/logger";
const SESSION_DURATION = 1000 * 60 * 60 * 24 * 30; // 30 days
const SESSION_DURATION = 60 * 60 * 24 * 30; // 30 days in seconds
export class AuthService {
/**
@@ -30,7 +30,7 @@ export class AuthService {
logger.info(`User registered: ${username}`);
const sessionId = crypto.randomUUID();
const expiresAt = new Date(Date.now() + SESSION_DURATION).getTime();
const expiresAt = Math.floor(Date.now() / 1000) + SESSION_DURATION;
await db.insert(sessionsTable).values({
id: sessionId,
@@ -66,7 +66,7 @@ export class AuthService {
}
const sessionId = crypto.randomUUID();
const expiresAt = new Date(Date.now() + SESSION_DURATION).getTime();
const expiresAt = Math.floor(Date.now() / 1000) + SESSION_DURATION;
await db.insert(sessionsTable).values({
id: sessionId,
@@ -112,7 +112,7 @@ export class AuthService {
return null;
}
if (session.session.expiresAt < Date.now()) {
if (session.session.expiresAt < Math.floor(Date.now() / 1000)) {
await db.delete(sessionsTable).where(eq(sessionsTable.id, sessionId));
return null;
}
@@ -134,7 +134,10 @@ export class AuthService {
* Clean up expired sessions
*/
async cleanupExpiredSessions() {
const result = await db.delete(sessionsTable).where(lt(sessionsTable.expiresAt, Date.now())).returning();
const result = await db
.delete(sessionsTable)
.where(lt(sessionsTable.expiresAt, Math.floor(Date.now() / 1000)))
.returning();
if (result.length > 0) {
logger.info(`Cleaned up ${result.length} expired sessions`);
}

View File

@@ -21,12 +21,12 @@ const calculateNextRun = (cronExpression: string): number => {
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
return interval.next().getTime();
return Math.floor(interval.next().getTime() / 1000);
} catch (error) {
logger.error(`Failed to parse cron expression "${cronExpression}": ${error}`);
const fallback = new Date();
fallback.setMinutes(fallback.getMinutes() + 1);
return fallback.getTime();
return Math.floor(fallback.getTime() / 1000);
}
};
@@ -126,7 +126,7 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
const [updated] = await db
.update(backupSchedulesTable)
.set({ ...data, nextBackupAt, updatedAt: Date.now() })
.set({ ...data, nextBackupAt, updatedAt: Math.floor(Date.now() / 1000) })
.where(eq(backupSchedulesTable.id, scheduleId))
.returning();
@@ -209,7 +209,12 @@ const executeBackup = async (scheduleId: number, manual = false) => {
await db
.update(backupSchedulesTable)
.set({ lastBackupStatus: "in_progress", updatedAt: Date.now(), lastBackupError: null, nextBackupAt })
.set({
lastBackupStatus: "in_progress",
updatedAt: Math.floor(Date.now() / 1000),
lastBackupError: null,
nextBackupAt,
})
.where(eq(backupSchedulesTable.id, scheduleId));
const abortController = new AbortController();
@@ -257,11 +262,11 @@ const executeBackup = async (scheduleId: number, manual = false) => {
await db
.update(backupSchedulesTable)
.set({
lastBackupAt: Date.now(),
lastBackupAt: Math.floor(Date.now() / 1000),
lastBackupStatus: exitCode === 0 ? "success" : "warning",
lastBackupError: null,
nextBackupAt: nextBackupAt,
updatedAt: Date.now(),
updatedAt: Math.floor(Date.now() / 1000),
})
.where(eq(backupSchedulesTable.id, scheduleId));
@@ -292,10 +297,10 @@ const executeBackup = async (scheduleId: number, manual = false) => {
await db
.update(backupSchedulesTable)
.set({
lastBackupAt: Date.now(),
lastBackupAt: Math.floor(Date.now() / 1000),
lastBackupStatus: "error",
lastBackupError: toMessage(error),
updatedAt: Date.now(),
updatedAt: Math.floor(Date.now() / 1000),
})
.where(eq(backupSchedulesTable.id, scheduleId));
@@ -323,7 +328,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
};
const getSchedulesToExecute = async () => {
const now = Date.now();
const now = Math.floor(Date.now() / 1000);
const schedules = await db.query.backupSchedulesTable.findMany({
where: eq(backupSchedulesTable.enabled, true),
});
@@ -362,7 +367,7 @@ const stopBackup = async (scheduleId: number) => {
.set({
lastBackupStatus: "error",
lastBackupError: "Backup was stopped by user",
updatedAt: Date.now(),
updatedAt: Math.floor(Date.now() / 1000),
})
.where(eq(backupSchedulesTable.id, scheduleId));

View File

@@ -105,7 +105,7 @@ const createRepository = async (name: string, config: RepositoryConfig, compress
if (!error) {
await db
.update(repositoriesTable)
.set({ status: "healthy", lastChecked: Date.now(), lastError: null })
.set({ status: "healthy", lastChecked: Math.floor(Date.now() / 1000), lastError: null })
.where(eq(repositoriesTable.id, id));
return { repository: created, status: 201 };
@@ -258,7 +258,7 @@ const checkHealth = async (repositoryId: string) => {
.update(repositoriesTable)
.set({
status,
lastChecked: Date.now(),
lastChecked: Math.floor(Date.now() / 1000),
lastError: error,
})
.where(eq(repositoriesTable.id, repository.id));
@@ -335,7 +335,7 @@ const doctorRepository = async (name: string) => {
.update(repositoriesTable)
.set({
status: allSuccessful ? "healthy" : "error",
lastChecked: Date.now(),
lastChecked: Math.floor(Date.now() / 1000),
lastError: allSuccessful ? null : steps.find((s) => !s.success)?.error,
})
.where(eq(repositoriesTable.id, repository.id));

View File

@@ -57,7 +57,7 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
await db
.update(volumesTable)
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
.where(eq(volumesTable.name, slug));
return { volume: created, status: 201 };
@@ -91,7 +91,7 @@ const mountVolume = async (name: string) => {
await db
.update(volumesTable)
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
.where(eq(volumesTable.name, name));
if (status === "mounted") {
@@ -196,7 +196,7 @@ const updateVolume = async (name: string, volumeData: UpdateVolumeBody) => {
const { error, status } = await backend.mount();
await db
.update(volumesTable)
.set({ status, lastError: error ?? null, lastHealthCheck: Date.now() })
.set({ status, lastError: error ?? null, lastHealthCheck: Math.floor(Date.now() / 1000) })
.where(eq(volumesTable.id, existing.id));
serverEvents.emit("volume:updated", { volumeName: updated.name });
@@ -255,7 +255,7 @@ const checkHealth = async (name: string) => {
await db
.update(volumesTable)
.set({ lastHealthCheck: Date.now(), status, lastError: error ?? null })
.set({ lastHealthCheck: Math.floor(Date.now() / 1000), status, lastError: error ?? null })
.where(eq(volumesTable.name, volume.name));
return { status, error };