be consistent
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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{
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -77,7 +77,6 @@ describe('SearchPage', () => {
|
||||
series: 'Earthsea',
|
||||
file_type: 'epub',
|
||||
file_size: '1 MB',
|
||||
upload_date: '2025-01-01',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -37,7 +37,6 @@ type SearchItem struct {
|
||||
Series string
|
||||
FileType string
|
||||
FileSize string
|
||||
UploadDate string
|
||||
}
|
||||
|
||||
type searchFunc func(query string) (searchResults []SearchItem, err error)
|
||||
|
||||
Reference in New Issue
Block a user