be consistent
This commit is contained in:
@@ -25,6 +25,8 @@ Regenerate:
|
|||||||
- `cd frontend && pnpm run generate:api`
|
- `cd frontend && pnpm run generate:api`
|
||||||
|
|
||||||
Notes:
|
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.
|
- 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:
|
Examples of generated files:
|
||||||
|
|||||||
+1
-9
@@ -68,18 +68,10 @@ func (s *Server) GetActivity(ctx context.Context, request GetActivityRequestObje
|
|||||||
|
|
||||||
apiActivities := make([]Activity, len(activities))
|
apiActivities := make([]Activity, len(activities))
|
||||||
for i, a := range 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{
|
apiActivities[i] = Activity{
|
||||||
DocumentId: a.DocumentID,
|
DocumentId: a.DocumentID,
|
||||||
DeviceId: a.DeviceID,
|
DeviceId: a.DeviceID,
|
||||||
StartTime: startTimeStr,
|
StartTime: parseTimeAny(a.StartTime),
|
||||||
Title: a.Title,
|
Title: a.Title,
|
||||||
Author: a.Author,
|
Author: a.Author,
|
||||||
Duration: a.Duration,
|
Duration: a.Duration,
|
||||||
|
|||||||
+10
-11
@@ -152,7 +152,7 @@ type Activity struct {
|
|||||||
EndPercentage float32 `json:"end_percentage"`
|
EndPercentage float32 `json:"end_percentage"`
|
||||||
ReadPercentage float32 `json:"read_percentage"`
|
ReadPercentage float32 `json:"read_percentage"`
|
||||||
StartPercentage float32 `json:"start_percentage"`
|
StartPercentage float32 `json:"start_percentage"`
|
||||||
StartTime string `json:"start_time"`
|
StartTime time.Time `json:"start_time"`
|
||||||
Title *string `json:"title,omitempty"`
|
Title *string `json:"title,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,10 +200,10 @@ type DatabaseInfo struct {
|
|||||||
|
|
||||||
// Device defines model for Device.
|
// Device defines model for Device.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
DeviceName *string `json:"device_name,omitempty"`
|
DeviceName string `json:"device_name"`
|
||||||
Id *string `json:"id,omitempty"`
|
Id string `json:"id"`
|
||||||
LastSynced *time.Time `json:"last_synced,omitempty"`
|
LastSynced time.Time `json:"last_synced"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirectoryItem defines model for DirectoryItem.
|
// DirectoryItem defines model for DirectoryItem.
|
||||||
@@ -357,14 +357,14 @@ type OperationType string
|
|||||||
// Progress defines model for Progress.
|
// Progress defines model for Progress.
|
||||||
type Progress struct {
|
type Progress struct {
|
||||||
Author *string `json:"author,omitempty"`
|
Author *string `json:"author,omitempty"`
|
||||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
DeviceId *string `json:"device_id,omitempty"`
|
DeviceId *string `json:"device_id,omitempty"`
|
||||||
DeviceName *string `json:"device_name,omitempty"`
|
DeviceName string `json:"device_name"`
|
||||||
DocumentId *string `json:"document_id,omitempty"`
|
DocumentId string `json:"document_id"`
|
||||||
Percentage *float64 `json:"percentage,omitempty"`
|
Percentage float64 `json:"percentage"`
|
||||||
Progress *string `json:"progress,omitempty"`
|
Progress *string `json:"progress,omitempty"`
|
||||||
Title *string `json:"title,omitempty"`
|
Title *string `json:"title,omitempty"`
|
||||||
UserId *string `json:"user_id,omitempty"`
|
UserId string `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProgressListResponse defines model for ProgressListResponse.
|
// ProgressListResponse defines model for ProgressListResponse.
|
||||||
@@ -391,7 +391,6 @@ type SearchItem struct {
|
|||||||
Language *string `json:"language,omitempty"`
|
Language *string `json:"language,omitempty"`
|
||||||
Series *string `json:"series,omitempty"`
|
Series *string `json:"series,omitempty"`
|
||||||
Title *string `json:"title,omitempty"`
|
Title *string `json:"title,omitempty"`
|
||||||
UploadDate *string `json:"upload_date,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchResponse defines model for SearchResponse.
|
// SearchResponse defines model for SearchResponse.
|
||||||
|
|||||||
+12
-2
@@ -106,6 +106,12 @@ components:
|
|||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
required:
|
||||||
|
- device_name
|
||||||
|
- percentage
|
||||||
|
- document_id
|
||||||
|
- user_id
|
||||||
|
- created_at
|
||||||
|
|
||||||
UpdateProgressRequest:
|
UpdateProgressRequest:
|
||||||
type: object
|
type: object
|
||||||
@@ -198,6 +204,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
start_time:
|
start_time:
|
||||||
type: string
|
type: string
|
||||||
|
format: date-time
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
author:
|
author:
|
||||||
@@ -240,8 +247,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
file_size:
|
file_size:
|
||||||
type: string
|
type: string
|
||||||
upload_date:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
SearchResponse:
|
SearchResponse:
|
||||||
type: object
|
type: object
|
||||||
@@ -384,6 +389,11 @@ components:
|
|||||||
last_synced:
|
last_synced:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- device_name
|
||||||
|
- created_at
|
||||||
|
- last_synced
|
||||||
|
|
||||||
SettingsResponse:
|
SettingsResponse:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
+10
-10
@@ -68,11 +68,11 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
|
|||||||
apiProgress[i] = Progress{
|
apiProgress[i] = Progress{
|
||||||
Title: row.Title,
|
Title: row.Title,
|
||||||
Author: row.Author,
|
Author: row.Author,
|
||||||
DeviceName: &row.DeviceName,
|
DeviceName: row.DeviceName,
|
||||||
Percentage: &row.Percentage,
|
Percentage: row.Percentage,
|
||||||
DocumentId: &row.DocumentID,
|
DocumentId: row.DocumentID,
|
||||||
UserId: &row.UserID,
|
UserId: row.UserID,
|
||||||
CreatedAt: parseTimePtr(row.CreatedAt),
|
CreatedAt: parseTimeAny(row.CreatedAt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,13 +105,13 @@ func (s *Server) GetProgress(ctx context.Context, request GetProgressRequestObje
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiProgress := Progress{
|
apiProgress := Progress{
|
||||||
DeviceName: &row.DeviceName,
|
DeviceName: row.DeviceName,
|
||||||
DeviceId: &row.DeviceID,
|
DeviceId: &row.DeviceID,
|
||||||
Percentage: &row.Percentage,
|
Percentage: row.Percentage,
|
||||||
Progress: &row.Progress,
|
Progress: &row.Progress,
|
||||||
DocumentId: &row.DocumentID,
|
DocumentId: row.DocumentID,
|
||||||
UserId: &row.UserID,
|
UserId: row.UserID,
|
||||||
CreatedAt: parseTimePtr(row.CreatedAt),
|
CreatedAt: parseTime(row.CreatedAt),
|
||||||
}
|
}
|
||||||
|
|
||||||
response := ProgressResponse{
|
response := ProgressResponse{
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ func (s *Server) GetSearch(ctx context.Context, request GetSearchRequestObject)
|
|||||||
Series: ptrOf(item.Series),
|
Series: ptrOf(item.Series),
|
||||||
FileType: ptrOf(item.FileType),
|
FileType: ptrOf(item.FileType),
|
||||||
FileSize: ptrOf(item.FileSize),
|
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))
|
apiDevices := make([]Device, len(devices))
|
||||||
for i, device := range devices {
|
for i, device := range devices {
|
||||||
apiDevices[i] = Device{
|
apiDevices[i] = Device{
|
||||||
Id: &device.ID,
|
Id: device.ID,
|
||||||
DeviceName: &device.DeviceName,
|
DeviceName: device.DeviceName,
|
||||||
CreatedAt: parseTimePtr(device.CreatedAt),
|
CreatedAt: parseTimeAny(device.CreatedAt),
|
||||||
LastSynced: parseTimePtr(device.LastSynced),
|
LastSynced: parseTimeAny(device.LastSynced),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,10 +140,10 @@ func (s *Server) UpdateSettings(ctx context.Context, request UpdateSettingsReque
|
|||||||
apiDevices := make([]Device, len(devices))
|
apiDevices := make([]Device, len(devices))
|
||||||
for i, device := range devices {
|
for i, device := range devices {
|
||||||
apiDevices[i] = Device{
|
apiDevices[i] = Device{
|
||||||
Id: &device.ID,
|
Id: device.ID,
|
||||||
DeviceName: &device.DeviceName,
|
DeviceName: device.DeviceName,
|
||||||
CreatedAt: parseTimePtr(device.CreatedAt),
|
CreatedAt: parseTimeAny(device.CreatedAt),
|
||||||
LastSynced: parseTimePtr(device.LastSynced),
|
LastSynced: parseTimeAny(device.LastSynced),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ func parseTime(s string) time.Time {
|
|||||||
return t
|
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
|
// parseTimePtr parses an interface{} (from SQL) to *time.Time
|
||||||
func parseTimePtr(v interface{}) *time.Time {
|
func parseTimePtr(v interface{}) *time.Time {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface Device {
|
export interface Device {
|
||||||
id?: string;
|
id: string;
|
||||||
device_name?: string;
|
device_name: string;
|
||||||
created_at?: string;
|
created_at: string;
|
||||||
last_synced?: string;
|
last_synced: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
export interface Progress {
|
export interface Progress {
|
||||||
title?: string;
|
title?: string;
|
||||||
author?: string;
|
author?: string;
|
||||||
device_name?: string;
|
device_name: string;
|
||||||
device_id?: string;
|
device_id?: string;
|
||||||
percentage?: number;
|
percentage: number;
|
||||||
progress?: string;
|
progress?: string;
|
||||||
document_id?: string;
|
document_id: string;
|
||||||
user_id?: string;
|
user_id: string;
|
||||||
created_at?: string;
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,4 @@ export interface SearchItem {
|
|||||||
series?: string;
|
series?: string;
|
||||||
file_type?: string;
|
file_type?: string;
|
||||||
file_size?: string;
|
file_size?: string;
|
||||||
upload_date?: string;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function ProgressPage() {
|
|||||||
{
|
{
|
||||||
id: 'percentage',
|
id: 'percentage',
|
||||||
header: 'Percentage',
|
header: 'Percentage',
|
||||||
render: row => (typeof row.percentage === 'number' ? `${Math.round(row.percentage)}%` : '0%'),
|
render: row => `${Math.round(row.percentage)}%`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'created_at',
|
id: 'created_at',
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ describe('SearchPage', () => {
|
|||||||
series: 'Earthsea',
|
series: 'Earthsea',
|
||||||
file_type: 'epub',
|
file_type: 'epub',
|
||||||
file_size: '1 MB',
|
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 { inputClassName } from '../components/TextInput';
|
||||||
import { useDebouncedState } from '../hooks/useDebouncedState';
|
import { useDebouncedState } from '../hooks/useDebouncedState';
|
||||||
import { Search2Icon, BookIcon } from '../icons';
|
import { Search2Icon, BookIcon } from '../icons';
|
||||||
import { formatDate } from '../utils/formatters';
|
|
||||||
import { dataForStatus } from '../utils/apiResponses';
|
import { dataForStatus } from '../utils/apiResponses';
|
||||||
|
|
||||||
const searchColumns: Column<SearchItem>[] = [
|
const searchColumns: Column<SearchItem>[] = [
|
||||||
@@ -18,12 +17,6 @@ const searchColumns: Column<SearchItem>[] = [
|
|||||||
{ id: 'series', header: 'Series', render: item => item.series || 'N/A' },
|
{ id: 'series', header: 'Series', render: item => item.series || 'N/A' },
|
||||||
{ id: 'type', header: 'Type', render: item => item.file_type || '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: '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 {
|
interface SearchPageViewProps {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ type SearchItem struct {
|
|||||||
Series string
|
Series string
|
||||||
FileType string
|
FileType string
|
||||||
FileSize string
|
FileSize string
|
||||||
UploadDate string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type searchFunc func(query string) (searchResults []SearchItem, err error)
|
type searchFunc func(query string) (searchResults []SearchItem, err error)
|
||||||
|
|||||||
Reference in New Issue
Block a user