be consistent

This commit is contained in:
2026-07-03 20:35:45 -04:00
parent 1ff96ee9c8
commit 9158648f3f
15 changed files with 80 additions and 80 deletions
+2
View File
@@ -25,6 +25,8 @@ Regenerate:
- `cd frontend && pnpm run generate:api`
Notes:
- `format: date-time` diverges by generator: **oapi-codegen (Go)** emits `time.Time`, while **orval (TS)** keeps `string`. Adding it to an existing string field will break Go handlers until they supply a `time.Time` (see `parseTime` / `parseTimeAny` in `api/v1/utils.go`).
- Marking a schema field `required` flips the generated Go type from a pointer to a value; update every handler build site to drop the `&`. Only require a field when **all** endpoints sharing that schema populate it — `Progress` is populated with different subsets by its list vs single handlers.
- If you add response headers in `api/v1/openapi.yaml` (for example `Set-Cookie`), `oapi-codegen` will generate typed response header structs in `api/v1/api.gen.go`; update the handler response values to populate those headers explicitly.
Examples of generated files:
+1 -9
View File
@@ -68,18 +68,10 @@ func (s *Server) GetActivity(ctx context.Context, request GetActivityRequestObje
apiActivities := make([]Activity, len(activities))
for i, a := range activities {
// Convert StartTime from interface{} to string
startTimeStr := ""
if a.StartTime != nil {
if str, ok := a.StartTime.(string); ok {
startTimeStr = str
}
}
apiActivities[i] = Activity{
DocumentId: a.DocumentID,
DeviceId: a.DeviceID,
StartTime: startTimeStr,
StartTime: parseTimeAny(a.StartTime),
Title: a.Title,
Author: a.Author,
Duration: a.Duration,
+29 -30
View File
@@ -145,15 +145,15 @@ func (e GetSearchParamsSource) Valid() bool {
// Activity defines model for Activity.
type Activity struct {
Author *string `json:"author,omitempty"`
DeviceId string `json:"device_id"`
DocumentId string `json:"document_id"`
Duration int64 `json:"duration"`
EndPercentage float32 `json:"end_percentage"`
ReadPercentage float32 `json:"read_percentage"`
StartPercentage float32 `json:"start_percentage"`
StartTime string `json:"start_time"`
Title *string `json:"title,omitempty"`
Author *string `json:"author,omitempty"`
DeviceId string `json:"device_id"`
DocumentId string `json:"document_id"`
Duration int64 `json:"duration"`
EndPercentage float32 `json:"end_percentage"`
ReadPercentage float32 `json:"read_percentage"`
StartPercentage float32 `json:"start_percentage"`
StartTime time.Time `json:"start_time"`
Title *string `json:"title,omitempty"`
}
// ActivityResponse defines model for ActivityResponse.
@@ -200,10 +200,10 @@ type DatabaseInfo struct {
// Device defines model for Device.
type Device struct {
CreatedAt *time.Time `json:"created_at,omitempty"`
DeviceName *string `json:"device_name,omitempty"`
Id *string `json:"id,omitempty"`
LastSynced *time.Time `json:"last_synced,omitempty"`
CreatedAt time.Time `json:"created_at"`
DeviceName string `json:"device_name"`
Id string `json:"id"`
LastSynced time.Time `json:"last_synced"`
}
// DirectoryItem defines model for DirectoryItem.
@@ -356,15 +356,15 @@ type OperationType string
// Progress defines model for Progress.
type Progress struct {
Author *string `json:"author,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
DeviceId *string `json:"device_id,omitempty"`
DeviceName *string `json:"device_name,omitempty"`
DocumentId *string `json:"document_id,omitempty"`
Percentage *float64 `json:"percentage,omitempty"`
Progress *string `json:"progress,omitempty"`
Title *string `json:"title,omitempty"`
UserId *string `json:"user_id,omitempty"`
Author *string `json:"author,omitempty"`
CreatedAt time.Time `json:"created_at"`
DeviceId *string `json:"device_id,omitempty"`
DeviceName string `json:"device_name"`
DocumentId string `json:"document_id"`
Percentage float64 `json:"percentage"`
Progress *string `json:"progress,omitempty"`
Title *string `json:"title,omitempty"`
UserId string `json:"user_id"`
}
// ProgressListResponse defines model for ProgressListResponse.
@@ -384,14 +384,13 @@ type ProgressResponse struct {
// SearchItem defines model for SearchItem.
type SearchItem struct {
Author *string `json:"author,omitempty"`
FileSize *string `json:"file_size,omitempty"`
FileType *string `json:"file_type,omitempty"`
Id *string `json:"id,omitempty"`
Language *string `json:"language,omitempty"`
Series *string `json:"series,omitempty"`
Title *string `json:"title,omitempty"`
UploadDate *string `json:"upload_date,omitempty"`
Author *string `json:"author,omitempty"`
FileSize *string `json:"file_size,omitempty"`
FileType *string `json:"file_type,omitempty"`
Id *string `json:"id,omitempty"`
Language *string `json:"language,omitempty"`
Series *string `json:"series,omitempty"`
Title *string `json:"title,omitempty"`
}
// SearchResponse defines model for SearchResponse.
+12 -2
View File
@@ -106,6 +106,12 @@ components:
created_at:
type: string
format: date-time
required:
- device_name
- percentage
- document_id
- user_id
- created_at
UpdateProgressRequest:
type: object
@@ -198,6 +204,7 @@ components:
type: string
start_time:
type: string
format: date-time
title:
type: string
author:
@@ -240,8 +247,6 @@ components:
type: string
file_size:
type: string
upload_date:
type: string
SearchResponse:
type: object
@@ -384,6 +389,11 @@ components:
last_synced:
type: string
format: date-time
required:
- id
- device_name
- created_at
- last_synced
SettingsResponse:
type: object
+10 -10
View File
@@ -68,11 +68,11 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
apiProgress[i] = Progress{
Title: row.Title,
Author: row.Author,
DeviceName: &row.DeviceName,
Percentage: &row.Percentage,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
DeviceName: row.DeviceName,
Percentage: row.Percentage,
DocumentId: row.DocumentID,
UserId: row.UserID,
CreatedAt: parseTimeAny(row.CreatedAt),
}
}
@@ -105,13 +105,13 @@ func (s *Server) GetProgress(ctx context.Context, request GetProgressRequestObje
}
apiProgress := Progress{
DeviceName: &row.DeviceName,
DeviceName: row.DeviceName,
DeviceId: &row.DeviceID,
Percentage: &row.Percentage,
Percentage: row.Percentage,
Progress: &row.Progress,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
DocumentId: row.DocumentID,
UserId: row.UserID,
CreatedAt: parseTime(row.CreatedAt),
}
response := ProgressResponse{
-1
View File
@@ -38,7 +38,6 @@ func (s *Server) GetSearch(ctx context.Context, request GetSearchRequestObject)
Series: ptrOf(item.Series),
FileType: ptrOf(item.FileType),
FileSize: ptrOf(item.FileSize),
UploadDate: ptrOf(item.UploadDate),
}
}
+8 -8
View File
@@ -29,10 +29,10 @@ func (s *Server) GetSettings(ctx context.Context, request GetSettingsRequestObje
apiDevices := make([]Device, len(devices))
for i, device := range devices {
apiDevices[i] = Device{
Id: &device.ID,
DeviceName: &device.DeviceName,
CreatedAt: parseTimePtr(device.CreatedAt),
LastSynced: parseTimePtr(device.LastSynced),
Id: device.ID,
DeviceName: device.DeviceName,
CreatedAt: parseTimeAny(device.CreatedAt),
LastSynced: parseTimeAny(device.LastSynced),
}
}
@@ -140,10 +140,10 @@ func (s *Server) UpdateSettings(ctx context.Context, request UpdateSettingsReque
apiDevices := make([]Device, len(devices))
for i, device := range devices {
apiDevices[i] = Device{
Id: &device.ID,
DeviceName: &device.DeviceName,
CreatedAt: parseTimePtr(device.CreatedAt),
LastSynced: parseTimePtr(device.LastSynced),
Id: device.ID,
DeviceName: device.DeviceName,
CreatedAt: parseTimeAny(device.CreatedAt),
LastSynced: parseTimeAny(device.LastSynced),
}
}
+8
View File
@@ -68,6 +68,14 @@ func parseTime(s string) time.Time {
return t
}
// parseTimeAny parses an interface{} (from SQL) to time.Time, zero on failure.
func parseTimeAny(v interface{}) time.Time {
if s, ok := v.(string); ok {
return parseTime(s)
}
return time.Time{}
}
// parseTimePtr parses an interface{} (from SQL) to *time.Time
func parseTimePtr(v interface{}) *time.Time {
if v == nil {
+4 -4
View File
@@ -7,8 +7,8 @@
*/
export interface Device {
id?: string;
device_name?: string;
created_at?: string;
last_synced?: string;
id: string;
device_name: string;
created_at: string;
last_synced: string;
}
+5 -5
View File
@@ -9,11 +9,11 @@
export interface Progress {
title?: string;
author?: string;
device_name?: string;
device_name: string;
device_id?: string;
percentage?: number;
percentage: number;
progress?: string;
document_id?: string;
user_id?: string;
created_at?: string;
document_id: string;
user_id: string;
created_at: string;
}
@@ -14,5 +14,4 @@ export interface SearchItem {
series?: string;
file_type?: string;
file_size?: string;
upload_date?: string;
}
+1 -1
View File
@@ -26,7 +26,7 @@ export default function ProgressPage() {
{
id: 'percentage',
header: 'Percentage',
render: row => (typeof row.percentage === 'number' ? `${Math.round(row.percentage)}%` : '0%'),
render: row => `${Math.round(row.percentage)}%`,
},
{
id: 'created_at',
-1
View File
@@ -77,7 +77,6 @@ describe('SearchPage', () => {
series: 'Earthsea',
file_type: 'epub',
file_size: '1 MB',
upload_date: '2025-01-01',
},
],
},
-7
View File
@@ -6,7 +6,6 @@ import { Button, Table, type Column, TextInput, IconInput } from '../components'
import { inputClassName } from '../components/TextInput';
import { useDebouncedState } from '../hooks/useDebouncedState';
import { Search2Icon, BookIcon } from '../icons';
import { formatDate } from '../utils/formatters';
import { dataForStatus } from '../utils/apiResponses';
const searchColumns: Column<SearchItem>[] = [
@@ -18,12 +17,6 @@ const searchColumns: Column<SearchItem>[] = [
{ id: 'series', header: 'Series', render: item => item.series || 'N/A' },
{ id: 'type', header: 'Type', render: item => item.file_type || 'N/A' },
{ id: 'size', header: 'Size', render: item => item.file_size || 'N/A' },
{
id: 'date',
header: 'Date',
className: 'hidden md:table-cell',
render: item => formatDate(item.upload_date),
},
];
interface SearchPageViewProps {
-1
View File
@@ -37,7 +37,6 @@ type SearchItem struct {
Series string
FileType string
FileSize string
UploadDate string
}
type searchFunc func(query string) (searchResults []SearchItem, err error)