diff --git a/api/ko-routes.go b/api/ko-routes.go index 60f25d3..8ac3898 100644 --- a/api/ko-routes.go +++ b/api/ko-routes.go @@ -260,7 +260,7 @@ func (api *API) addActivities(c *gin.Context) { UserID: rUser.(string), DocumentID: item.DocumentID, DeviceID: rActivity.DeviceID, - StartTime: time.Unix(int64(item.StartTime), 0).UTC(), + StartTime: time.Unix(int64(item.StartTime), 0).UTC().Format(time.RFC3339), Duration: int64(item.Duration), Page: int64(item.Page), Pages: int64(item.Pages), @@ -306,7 +306,7 @@ func (api *API) checkActivitySync(c *gin.Context) { ID: rCheckActivity.DeviceID, UserID: rUser.(string), DeviceName: rCheckActivity.Device, - LastSynced: time.Now().UTC(), + LastSynced: time.Now().UTC().Format(time.RFC3339), }); err != nil { log.Error("[checkActivitySync] UpsertDevice DB Error", err) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) @@ -319,15 +319,23 @@ func (api *API) checkActivitySync(c *gin.Context) { DeviceID: rCheckActivity.DeviceID, }) if err == sql.ErrNoRows { - lastActivity = time.UnixMilli(0) + lastActivity = time.UnixMilli(0).Format(time.RFC3339) } else if err != nil { log.Error("[checkActivitySync] GetLastActivity DB Error:", err) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) return } + // Parse Time + parsedTime, err := time.Parse(time.RFC3339, lastActivity) + if err != nil { + log.Error("[checkActivitySync] Time Parse Error:", err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + return + } + c.JSON(http.StatusOK, gin.H{ - "last_sync": lastActivity.Unix(), + "last_sync": parsedTime.Unix(), }) } @@ -406,7 +414,7 @@ func (api *API) checkDocumentsSync(c *gin.Context) { ID: rCheckDocs.DeviceID, UserID: rUser.(string), DeviceName: rCheckDocs.Device, - LastSynced: time.Now().UTC(), + LastSynced: time.Now().UTC().Format(time.RFC3339), }) if err != nil { log.Error("[checkDocumentsSync] UpsertDevice DB Error", err) diff --git a/database/models.go b/database/models.go index cd9a691..3b50be8 100644 --- a/database/models.go +++ b/database/models.go @@ -6,58 +6,57 @@ package database import ( "database/sql" - "time" ) type Activity struct { - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - CreatedAt time.Time `json:"created_at"` - StartTime time.Time `json:"start_time"` - Page int64 `json:"page"` - Pages int64 `json:"pages"` - Duration int64 `json:"duration"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + CreatedAt string `json:"created_at"` + StartTime string `json:"start_time"` + Page int64 `json:"page"` + Pages int64 `json:"pages"` + Duration int64 `json:"duration"` } type Device struct { - ID string `json:"id"` - UserID string `json:"user_id"` - DeviceName string `json:"device_name"` - LastSynced time.Time `json:"last_synced"` - CreatedAt string `json:"created_at"` - Sync bool `json:"sync"` + ID string `json:"id"` + UserID string `json:"user_id"` + DeviceName string `json:"device_name"` + LastSynced string `json:"last_synced"` + CreatedAt string `json:"created_at"` + Sync bool `json:"sync"` } type Document struct { - ID string `json:"id"` - Md5 *string `json:"md5"` - Filepath *string `json:"filepath"` - Coverfile *string `json:"coverfile"` - Title *string `json:"title"` - Author *string `json:"author"` - Series *string `json:"series"` - SeriesIndex *int64 `json:"series_index"` - Lang *string `json:"lang"` - Description *string `json:"description"` - Words *int64 `json:"words"` - Gbid *string `json:"gbid"` - Olid *string `json:"-"` - Isbn10 *string `json:"isbn10"` - Isbn13 *string `json:"isbn13"` - Synced bool `json:"-"` - Deleted bool `json:"-"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Md5 *string `json:"md5"` + Filepath *string `json:"filepath"` + Coverfile *string `json:"coverfile"` + Title *string `json:"title"` + Author *string `json:"author"` + Series *string `json:"series"` + SeriesIndex *int64 `json:"series_index"` + Lang *string `json:"lang"` + Description *string `json:"description"` + Words *int64 `json:"words"` + Gbid *string `json:"gbid"` + Olid *string `json:"-"` + Isbn10 *string `json:"isbn10"` + Isbn13 *string `json:"isbn13"` + Synced bool `json:"-"` + Deleted bool `json:"-"` + UpdatedAt string `json:"updated_at"` + CreatedAt string `json:"created_at"` } type DocumentProgress struct { - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - Percentage float64 `json:"percentage"` - Progress string `json:"progress"` - CreatedAt time.Time `json:"created_at"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + Percentage float64 `json:"percentage"` + Progress string `json:"progress"` + CreatedAt string `json:"created_at"` } type DocumentUserStatistic struct { @@ -74,36 +73,36 @@ type DocumentUserStatistic struct { } type Metadatum struct { - ID int64 `json:"id"` - DocumentID string `json:"document_id"` - Title *string `json:"title"` - Author *string `json:"author"` - Description *string `json:"description"` - Gbid *string `json:"gbid"` - Olid *string `json:"olid"` - Isbn10 *string `json:"isbn10"` - Isbn13 *string `json:"isbn13"` - CreatedAt time.Time `json:"created_at"` + ID int64 `json:"id"` + DocumentID string `json:"document_id"` + Title *string `json:"title"` + Author *string `json:"author"` + Description *string `json:"description"` + Gbid *string `json:"gbid"` + Olid *string `json:"olid"` + Isbn10 *string `json:"isbn10"` + Isbn13 *string `json:"isbn13"` + CreatedAt string `json:"created_at"` } type RawActivity struct { - ID int64 `json:"id"` - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - StartTime time.Time `json:"start_time"` - Page int64 `json:"page"` - Pages int64 `json:"pages"` - Duration int64 `json:"duration"` - CreatedAt time.Time `json:"created_at"` + ID int64 `json:"id"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + StartTime string `json:"start_time"` + Page int64 `json:"page"` + Pages int64 `json:"pages"` + Duration int64 `json:"duration"` + CreatedAt string `json:"created_at"` } type User struct { - ID string `json:"id"` - Pass *string `json:"-"` - Admin bool `json:"-"` - TimeOffset *string `json:"time_offset"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Pass *string `json:"-"` + Admin bool `json:"-"` + TimeOffset *string `json:"time_offset"` + CreatedAt string `json:"created_at"` } type UserStreak struct { @@ -120,7 +119,7 @@ type UserStreak struct { type ViewDocumentUserStatistic struct { DocumentID string `json:"document_id"` UserID string `json:"user_id"` - LastRead time.Time `json:"last_read"` + LastRead string `json:"last_read"` Page int64 `json:"page"` Pages int64 `json:"pages"` TotalTimeSeconds sql.NullFloat64 `json:"total_time_seconds"` @@ -131,14 +130,14 @@ type ViewDocumentUserStatistic struct { } type ViewRescaledActivity struct { - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - CreatedAt time.Time `json:"created_at"` - StartTime time.Time `json:"start_time"` - Page int64 `json:"page"` - Pages int64 `json:"pages"` - Duration int64 `json:"duration"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + CreatedAt string `json:"created_at"` + StartTime string `json:"start_time"` + Page int64 `json:"page"` + Pages int64 `json:"pages"` + Duration int64 `json:"duration"` } type ViewUserStreak struct { diff --git a/database/query.sql b/database/query.sql index 9ed1ac2..ba4b4bb 100644 --- a/database/query.sql +++ b/database/query.sql @@ -61,7 +61,7 @@ WITH filtered_activity AS ( SELECT document_id, - CAST(DATETIME(activity.start_time, users.time_offset) AS TEXT) AS start_time, + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', activity.start_time, users.time_offset) AS TEXT) AS start_time, title, author, duration, @@ -131,8 +131,8 @@ WHERE id = $device_id LIMIT 1; -- name: GetDevices :many SELECT devices.device_name, - CAST(DATETIME(devices.created_at, users.time_offset) AS TEXT) AS created_at, - CAST(DATETIME(devices.last_synced, users.time_offset) AS TEXT) AS last_synced + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.created_at, users.time_offset) AS TEXT) AS created_at, + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.last_synced, users.time_offset) AS TEXT) AS last_synced FROM devices JOIN users ON users.id = devices.user_id WHERE users.id = $user_id; @@ -192,7 +192,7 @@ SELECT COALESCE(dus.pages, 0) AS pages, COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, - DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) + STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset) AS last_read, CASE WHEN dus.percentage > 97.0 THEN 100.0 @@ -236,7 +236,7 @@ SELECT COALESCE(dus.pages, 0) AS pages, COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, - DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) + STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset) AS last_read, CASE WHEN dus.percentage > 97.0 THEN 100.0 diff --git a/database/query.sql.go b/database/query.sql.go index a8f10bb..e540825 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -9,7 +9,6 @@ import ( "context" "database/sql" "strings" - "time" ) const addActivity = `-- name: AddActivity :one @@ -27,13 +26,13 @@ RETURNING id, user_id, document_id, device_id, start_time, page, pages, duration ` type AddActivityParams struct { - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - StartTime time.Time `json:"start_time"` - Duration int64 `json:"duration"` - Page int64 `json:"page"` - Pages int64 `json:"pages"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + StartTime string `json:"start_time"` + Duration int64 `json:"duration"` + Page int64 `json:"page"` + Pages int64 `json:"pages"` } func (q *Queries) AddActivity(ctx context.Context, arg AddActivityParams) (RawActivity, error) { @@ -173,7 +172,7 @@ WITH filtered_activity AS ( SELECT document_id, - CAST(DATETIME(activity.start_time, users.time_offset) AS TEXT) AS start_time, + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', activity.start_time, users.time_offset) AS TEXT) AS start_time, title, author, duration, @@ -397,8 +396,8 @@ func (q *Queries) GetDevice(ctx context.Context, deviceID string) (Device, error const getDevices = `-- name: GetDevices :many SELECT devices.device_name, - CAST(DATETIME(devices.created_at, users.time_offset) AS TEXT) AS created_at, - CAST(DATETIME(devices.last_synced, users.time_offset) AS TEXT) AS last_synced + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.created_at, users.time_offset) AS TEXT) AS created_at, + CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.last_synced, users.time_offset) AS TEXT) AS last_synced FROM devices JOIN users ON users.id = devices.user_id WHERE users.id = ?1 @@ -501,9 +500,9 @@ AND start_time >= ?3 ` type GetDocumentReadStatsParams struct { - DocumentID string `json:"document_id"` - UserID string `json:"user_id"` - StartTime time.Time `json:"start_time"` + DocumentID string `json:"document_id"` + UserID string `json:"user_id"` + StartTime string `json:"start_time"` } type GetDocumentReadStatsRow struct { @@ -534,10 +533,10 @@ FROM capped_stats ` type GetDocumentReadStatsCappedParams struct { - PageDurationCap int64 `json:"page_duration_cap"` - DocumentID string `json:"document_id"` - UserID string `json:"user_id"` - StartTime time.Time `json:"start_time"` + PageDurationCap int64 `json:"page_duration_cap"` + DocumentID string `json:"document_id"` + UserID string `json:"user_id"` + StartTime string `json:"start_time"` } type GetDocumentReadStatsCappedRow struct { @@ -573,7 +572,7 @@ SELECT COALESCE(dus.pages, 0) AS pages, COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, - DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) + STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset) AS last_read, CASE WHEN dus.percentage > 97.0 THEN 100.0 @@ -715,7 +714,7 @@ SELECT COALESCE(dus.pages, 0) AS pages, COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, - DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) + STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset) AS last_read, CASE WHEN dus.percentage > 97.0 THEN 100.0 @@ -819,9 +818,9 @@ type GetLastActivityParams struct { UserID string `json:"user_id"` } -func (q *Queries) GetLastActivity(ctx context.Context, arg GetLastActivityParams) (time.Time, error) { +func (q *Queries) GetLastActivity(ctx context.Context, arg GetLastActivityParams) (string, error) { row := q.db.QueryRowContext(ctx, getLastActivity, arg.DeviceID, arg.UserID) - var start_time time.Time + var start_time string err := row.Scan(&start_time) return start_time, err } @@ -908,13 +907,13 @@ type GetProgressParams struct { } type GetProgressRow struct { - UserID string `json:"user_id"` - DocumentID string `json:"document_id"` - DeviceID string `json:"device_id"` - Percentage float64 `json:"percentage"` - Progress string `json:"progress"` - CreatedAt time.Time `json:"created_at"` - DeviceName string `json:"device_name"` + UserID string `json:"user_id"` + DocumentID string `json:"document_id"` + DeviceID string `json:"device_id"` + Percentage float64 `json:"percentage"` + Progress string `json:"progress"` + CreatedAt string `json:"created_at"` + DeviceName string `json:"device_name"` } func (q *Queries) GetProgress(ctx context.Context, arg GetProgressParams) (GetProgressRow, error) { @@ -1293,10 +1292,10 @@ RETURNING id, user_id, device_name, last_synced, created_at, sync ` type UpsertDeviceParams struct { - ID string `json:"id"` - UserID string `json:"user_id"` - LastSynced time.Time `json:"last_synced"` - DeviceName string `json:"device_name"` + ID string `json:"id"` + UserID string `json:"user_id"` + LastSynced string `json:"last_synced"` + DeviceName string `json:"device_name"` } func (q *Queries) UpsertDevice(ctx context.Context, arg UpsertDeviceParams) (Device, error) { diff --git a/database/schema.sql b/database/schema.sql index 2234167..aa66442 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS users ( admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)), time_offset TEXT NOT NULL DEFAULT '0 hours', - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')) ); -- Books / Documents @@ -39,8 +39,8 @@ CREATE TABLE IF NOT EXISTS documents ( synced BOOLEAN NOT NULL DEFAULT 0 CHECK (synced IN (0, 1)), deleted BOOLEAN NOT NULL DEFAULT 0 CHECK (deleted IN (0, 1)), - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + updated_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')) ); -- Metadata @@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS metadata ( isbn10 TEXT, isbn13 TEXT, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), FOREIGN KEY (document_id) REFERENCES documents (id) ); @@ -68,8 +68,8 @@ CREATE TABLE IF NOT EXISTS devices ( user_id TEXT NOT NULL, device_name TEXT NOT NULL, - last_synced DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_synced DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), sync BOOLEAN NOT NULL DEFAULT 1 CHECK (sync IN (0, 1)), FOREIGN KEY (user_id) REFERENCES users (id) @@ -83,7 +83,7 @@ CREATE TABLE IF NOT EXISTS document_progress ( percentage REAL NOT NULL, progress TEXT NOT NULL, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (document_id) REFERENCES documents (id), @@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS raw_activity ( page INTEGER NOT NULL, pages INTEGER NOT NULL, duration INTEGER NOT NULL, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')), FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (document_id) REFERENCES documents (id), @@ -430,6 +430,6 @@ INSERT INTO document_user_statistics SELECT * FROM view_document_user_statistics CREATE TRIGGER IF NOT EXISTS update_documents_updated_at BEFORE UPDATE ON documents BEGIN UPDATE documents -SET updated_at = CURRENT_TIMESTAMP +SET updated_at = STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = old.id; END; diff --git a/sqlc.yaml b/sqlc.yaml index 79db8ba..82de80c 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -124,6 +124,11 @@ sql: type: "string" pointer: true + # Override Time + - db_type: "DATETIME" + go_type: + type: "string" + # Do not generate JSON - column: "documents.synced" go_struct_tag: 'json:"-"'