diff --git a/app/client/modules/backups/components/schedule-summary.tsx b/app/client/modules/backups/components/schedule-summary.tsx
index 4422c77..7254368 100644
--- a/app/client/modules/backups/components/schedule-summary.tsx
+++ b/app/client/modules/backups/components/schedule-summary.tsx
@@ -148,13 +148,13 @@ export const ScheduleSummary = (props: Props) => {
Last backup
- {schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleString() : "Never"}
+ {schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleString() : "Never"}
Next backup
- {schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleString() : "Never"}
+ {schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleString() : "Never"}
diff --git a/app/client/modules/backups/routes/backups.tsx b/app/client/modules/backups/routes/backups.tsx
index 505d124..16fe07c 100644
--- a/app/client/modules/backups/routes/backups.tsx
+++ b/app/client/modules/backups/routes/backups.tsx
@@ -97,13 +97,13 @@ export default function Backups({ loaderData }: Route.ComponentProps) {
Last backup
- {schedule.lastBackupAt ? new Date(schedule.lastBackupAt).toLocaleDateString() : "Never"}
+ {schedule.lastBackupAt ? new Date(schedule.lastBackupAt * 1000).toLocaleDateString() : "Never"}
Next backup
- {schedule.nextBackupAt ? new Date(schedule.nextBackupAt).toLocaleDateString() : "N/A"}
+ {schedule.nextBackupAt ? new Date(schedule.nextBackupAt * 1000).toLocaleDateString() : "N/A"}
diff --git a/app/client/modules/repositories/tabs/info.tsx b/app/client/modules/repositories/tabs/info.tsx
index e081afc..fc60f21 100644
--- a/app/client/modules/repositories/tabs/info.tsx
+++ b/app/client/modules/repositories/tabs/info.tsx
@@ -32,12 +32,12 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
Created At
{new Date(repository.createdAt * 1000).toLocaleString()}
-
-
Last Checked
-
- {repository.lastChecked ? new Date(repository.lastChecked).toLocaleString() : "Never"}
-
-
+
+
Last Checked
+
+ {repository.lastChecked ? new Date(repository.lastChecked * 1000).toLocaleString() : "Never"}
+
+
{repository.lastError && (
diff --git a/app/client/modules/volumes/components/healthchecks-card.tsx b/app/client/modules/volumes/components/healthchecks-card.tsx
index 49c2e19..b0a261c 100644
--- a/app/client/modules/volumes/components/healthchecks-card.tsx
+++ b/app/client/modules/volumes/components/healthchecks-card.tsx
@@ -13,7 +13,7 @@ type Props = {
};
export const HealthchecksCard = ({ volume }: Props) => {
- const timeAgo = formatDistanceToNow(volume.lastHealthCheck, {
+ const timeAgo = formatDistanceToNow(volume.lastHealthCheck * 1000, {
addSuffix: true,
});
diff --git a/app/drizzle/0016_fix-timestamps-to-seconds.sql b/app/drizzle/0016_fix-timestamps-to-seconds.sql
new file mode 100644
index 0000000..38e25a1
--- /dev/null
+++ b/app/drizzle/0016_fix-timestamps-to-seconds.sql
@@ -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;
+
diff --git a/app/drizzle/meta/0016_snapshot.json b/app/drizzle/meta/0016_snapshot.json
new file mode 100644
index 0000000..01b65a3
--- /dev/null
+++ b/app/drizzle/meta/0016_snapshot.json
@@ -0,0 +1,688 @@
+{
+ "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": {}
+ }
+}
\ No newline at end of file
diff --git a/app/drizzle/meta/_journal.json b/app/drizzle/meta/_journal.json
index d69d66b..ad979e7 100644
--- a/app/drizzle/meta/_journal.json
+++ b/app/drizzle/meta/_journal.json
@@ -113,6 +113,13 @@
"when": 1764182465287,
"tag": "0015_jazzy_sersi",
"breakpoints": true
+ },
+ {
+ "idx": 16,
+ "version": "6",
+ "when": 1764193182689,
+ "tag": "0016_fix-timestamps-to-seconds",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/app/server/modules/auth/auth.controller.ts b/app/server/modules/auth/auth.controller.ts
index c78d281..b39079b 100644
--- a/app/server/modules/auth/auth.controller.ts
+++ b/app/server/modules/auth/auth.controller.ts
@@ -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({
diff --git a/app/server/modules/auth/auth.service.ts b/app/server/modules/auth/auth.service.ts
index 3b762ec..8a83c9d 100644
--- a/app/server/modules/auth/auth.service.ts
+++ b/app/server/modules/auth/auth.service.ts
@@ -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,7 @@ 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`);
}
diff --git a/app/server/modules/backups/backups.service.ts b/app/server/modules/backups/backups.service.ts
index e36bcf1..3525416 100644
--- a/app/server/modules/backups/backups.service.ts
+++ b/app/server/modules/backups/backups.service.ts
@@ -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,7 @@ 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 +257,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 +292,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 +323,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 +362,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));
diff --git a/app/server/modules/repositories/repositories.service.ts b/app/server/modules/repositories/repositories.service.ts
index c79f6e5..79ff267 100644
--- a/app/server/modules/repositories/repositories.service.ts
+++ b/app/server/modules/repositories/repositories.service.ts
@@ -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));
diff --git a/app/server/modules/volumes/volume.service.ts b/app/server/modules/volumes/volume.service.ts
index 7169d6f..94f1324 100644
--- a/app/server/modules/volumes/volume.service.ts
+++ b/app/server/modules/volumes/volume.service.ts
@@ -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 };