Files
TREK/server/src/types.ts
T
Maurice 7ee8333aeb refactor(server): types.ts keeps only imported types and derives Trip from the schema
Thirteen interfaces nobody imported are gone. Trip is now the selectable row
of the generated TripsTable, so currency, is_archived and reminder_days read
as the nullable columns they are and feed_token and reminder_sent_for exist.
2026-10-10 11:52:07 +02:00

345 lines
9.7 KiB
TypeScript

import type { Selectable } from 'kysely';
import type { TripsTable } from './db/kysely/Trips.table';
export interface User {
id: number;
username: string;
email: string;
role: 'admin' | 'user';
password_hash?: string;
maps_api_key?: string | null;
unsplash_api_key?: string | null;
/** Amap (高德) web-service key — the per-user fallback for the Amap provider. */
amap_api_key?: string | null;
openweather_api_key?: string | null;
avatar?: string | null;
oidc_sub?: string | null;
oidc_issuer?: string | null;
last_login?: string | null;
mfa_enabled?: number | boolean;
mfa_secret?: string | null;
mfa_backup_codes?: string | null;
must_change_password?: number | boolean;
first_seen_version?: string;
login_count?: number;
// Guest members (#1362): accountless trip participants. Flagged guests must never
// authenticate or appear in the global user directory.
is_guest?: number | boolean;
created_at?: string;
updated_at?: string;
}
/**
* A trips row as the database holds it, derived from the generated table type
* so it cannot drift from the schema (currency, is_archived and reminder_days
* are nullable columns, and feed_token and reminder_sent_for exist).
*/
export type Trip = Selectable<TripsTable>;
export interface Day {
id: number;
trip_id: number;
day_number: number;
date?: string | null;
notes?: string | null;
title?: string | null;
default_transport_mode?: string | null;
}
export interface Place {
id: number;
trip_id: number;
name: string;
description?: string | null;
lat?: number | null;
lng?: number | null;
address?: string | null;
category_id?: number | null;
price?: number | null;
currency?: string | null;
reservation_status?: string;
reservation_notes?: string | null;
reservation_datetime?: string | null;
place_time?: string | null;
end_time?: string | null;
duration_minutes?: number;
notes?: string | null;
image_url?: string | null;
google_place_id?: string | null;
google_ftid?: string | null;
osm_id?: string | null;
amap_poi_id?: string | null;
route_geometry?: string | null;
route_color?: string | null;
website?: string | null;
phone?: string | null;
/** Typed in by hand (#2472). */
email?: string | null;
/** Hand-kept opening hours as JSON text, seven days Monday first (#2472). */
opening_hours?: string | null;
transport_mode?: string;
/** What kind of stop this is on a drive (#1797); null for an ordinary place. */
stop_type?: string | null;
/** How full this stop fills the tank, 1-100; null follows the traveller's own setting. */
fill_percent?: number | null;
created_at?: string;
updated_at?: string;
}
export interface Tag {
id: number;
user_id: number;
name: string;
color: string;
created_at?: string;
}
export interface BudgetItemReceipt {
id: number;
filename: string;
original_name: string;
file_size?: number | null;
mime_type?: string | null;
url: string;
}
export interface BudgetItem {
id: number;
trip_id: number;
category: string;
name: string;
total_price: number;
currency?: string | null;
exchange_rate?: number;
persons?: number | null;
days?: number | null;
note?: string | null;
/** Itemized receipt for a per-item split, as JSON. Its own column since #1658. */
ticket_json?: string | null;
reservation_id?: number | null;
/** Set when the expense was created from a place (#1298) — the other side of
* the same link reservation_id is for a booking. */
place_id?: number | null;
paid_by_user_id?: number | null;
expense_date?: string | null;
sort_order: number;
created_at?: string;
members?: BudgetItemMember[];
payers?: BudgetItemPayer[];
receipts?: BudgetItemReceipt[];
}
export interface BudgetItemMember {
user_id: number;
paid: number;
username: string;
avatar_url?: string | null;
avatar?: string | null;
budget_item_id?: number;
amount?: number | null;
}
export interface BudgetItemPayer {
user_id: number;
amount: number;
username?: string;
avatar_url?: string | null;
avatar?: string | null;
budget_item_id?: number;
}
interface ReservationEndpoint {
id: number;
reservation_id: number;
role: 'from' | 'to' | 'stop';
sequence: number;
name: string;
code: string | null;
lat: number;
lng: number;
timezone: string | null;
local_time: string | null;
local_date: string | null;
}
export interface Reservation {
id: number;
trip_id: number;
day_id?: number | null;
end_day_id?: number | null;
place_id?: number | null;
assignment_id?: number | null;
title: string;
reservation_time?: string | null;
reservation_end_time?: string | null;
location?: string | null;
confirmation_number?: string | null;
notes?: string | null;
status: string;
type: string;
/**
* TEXT at the DB, not INTEGER (Plan 3d inventory §18.1 — no FK, some rows
* read back as `"14.0"`): every reader normalises it, so this stays a
* string here rather than the genuine INTEGER shape of
* `day_assignments.accommodation_id`. Do not "fix" this to `number`: that
* is a different column on a different table.
*/
accommodation_id?: string | null;
metadata?: string | null;
needs_review?: number;
endpoints?: ReservationEndpoint[];
created_at?: string;
day_number?: number;
place_name?: string;
}
export interface DayNote {
id: number;
day_id: number;
trip_id: number;
text: string;
time?: string | null;
icon: string;
/** One of NOTE_COLORS, or null for the neutral card (#1629). */
color?: string | null;
sort_order: number;
created_at?: string;
}
export interface Participant {
user_id: number;
username: string;
avatar?: string | null;
}
// ── Journey addon ─────────────────────────────────────────────────────────
export interface Journey {
id: number;
user_id: number;
title: string;
subtitle?: string | null;
cover_gradient?: string | null;
cover_image?: string | null;
status: 'draft' | 'active' | 'completed' | 'archived';
/** Draw the linked trips' GPX tracks on this journey's map (#2194). 0 by default. */
show_trip_tracks?: number;
/** The state the owner set by hand (#762); null follows the trip dates. */
status_override?: 'draft' | 'live' | 'completed' | null;
/** Entries without a place take the spot of their first geotagged photo (#1003). 0 by default. */
photo_location?: number;
created_at: number;
updated_at: number;
}
export interface JourneyEntry {
id: number;
journey_id: number;
source_trip_id?: number | null;
source_place_id?: number | null;
/**
* The `day_assignments` row this entry was derived from (#2329). A place standing
* on two days is two entries, and only this tells them apart. May dangle: an
* unassigned stop keeps the id so reconciliation can see the assignment is gone.
*/
source_assignment_id?: number | null;
author_id: number;
type: 'entry' | 'checkin' | 'skeleton';
title?: string | null;
story?: string | null;
entry_date: string;
entry_time?: string | null;
location_name?: string | null;
location_lat?: number | null;
location_lng?: number | null;
mood?: string | null;
weather?: string | null;
tags?: string | null;
pros_cons?: string | null;
visibility: 'private' | 'shared' | 'public';
sort_order: number;
/** ISO 3166-1 alpha-2, resolved from the coordinates when the entry was written. */
country_code?: string | null;
/**
* 0/1 as the row holds it. A suggestion the traveller waved away: the row stays,
* so the trip sync does not create it again, and every read leaves it out.
*/
dismissed: number;
/**
* 0/1 as the row holds it. Switched on, the entry stays in the journal but
* is left out of the route and the figures Studio prints (discussion #2064).
* The wire carries a boolean; journey-entry-row.ts is where the two meet.
*/
stats_excluded: number;
/** 1 while the entry is a draft (#696), kept off the public share page. */
is_draft?: number;
created_at: number;
updated_at: number;
}
export interface TrekPhoto {
id: number;
provider: string;
asset_id?: string | null;
owner_id?: number | null;
file_path?: string | null;
thumbnail_path?: string | null;
width?: number | null;
height?: number | null;
passphrase?: string | null;
/** 'image' (default) or 'video' — discriminates how the asset is served/played (#823). */
media_type?: string | null;
/** Optional video duration in milliseconds. */
duration_ms?: number | null;
/** When the picture was taken, as the provider or its EXIF reported it (#1614). */
taken_at?: string | null;
/** Capture coordinates. Stored as a pair or not at all. */
lat?: number | null;
lng?: number | null;
created_at: string;
}
export interface JourneyPhoto {
id: number;
entry_id: number;
photo_id: number;
caption?: string | null;
sort_order: number;
shared: number;
created_at: number;
// Joined from trek_photos for API responses
provider?: string;
asset_id?: string | null;
owner_id?: number | null;
file_path?: string | null;
thumbnail_path?: string | null;
width?: number | null;
height?: number | null;
}
export interface GalleryPhoto {
id: number;
journey_id: number;
photo_id: number;
caption?: string | null;
shared: number;
sort_order: number;
created_at: number;
// Joined from trek_photos for API responses
provider?: string;
asset_id?: string | null;
owner_id?: number | null;
file_path?: string | null;
thumbnail_path?: string | null;
width?: number | null;
height?: number | null;
}
export interface JourneyContributor {
journey_id: number;
user_id: number;
role: 'owner' | 'editor' | 'viewer';
added_at: number;
}