diff --git a/frontend/src/api/admin.ts b/frontend/src/api/admin.ts index cf52dc6..f783ab1 100644 --- a/frontend/src/api/admin.ts +++ b/frontend/src/api/admin.ts @@ -1,75 +1,63 @@ -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; -// User interface -interface User { - username: string - isAdmin: boolean - // Add other user-related fields as needed -} +/** A raw admin user row as returned by the API (positional from SQL columns) */ +export type AdminUserRow = [ + name: string, + isAdmin: boolean, +]; /** - * Get all users (admin only) - * @param token - Authentication token - * @returns Array of users or null if operation failed + * Fetch all users (admin only). + * + * @param token - The auth token (must belong to an admin user) + * @returns An array of {@link AdminUserRow} on success, or `undefined` on failure */ -export async function get(token: string): Promise { - return apiWrapper('/api/admin/get', { token }); +export async function get(token: string): Promise { + return apiWrapper('/api/admin/get', { token }); } /** - * Add new user (admin only) - * @param token - Authentication token - * @param username - Username - * @returns Created user or null if operation failed + * Create a new user (admin only). + * + * @param token - The auth token (must belong to an admin user) + * @param username - The new username + * @returns A single {@link AdminUserRow} of the newly created user on success, or `undefined` on failure */ -export async function add(token: string, username: string): Promise { - return apiWrapper('/api/admin/add', { token, username }); +export async function add(token: string, username: string): Promise { + return apiWrapper('/api/admin/add', { token, username }); } /** - * Update user (admin only) - * @param token - Authentication token - * @param username - Username - * @param params - Update parameters (partial) - * @returns true if update successful, false otherwise + * Update an existing user's password and/or admin status (admin only). + * + * @param token - The auth token (must belong to an admin user) + * @param username - The target user's username + * @param password - (optional) The new password (plaintext, will be hashed server-side) + * @param isAdmin - (optional) Whether the user should be an admin + * @returns `true` on success, `false` on failure (or if no changes were provided) */ export async function update( token: string, username: string, - params: { - password?: string - isAdmin?: boolean - } + password?: string, + isAdmin?: boolean, ): Promise { - const data: any = { - token, - username - }; + const data: Record = { token, username }; + if (password !== undefined) data.password = password; + if (isAdmin !== undefined) data.isAdmin = isAdmin; - let count = 0; - if (typeof params.password !== 'undefined') { - data.password = params.password; - count++; - } - if (typeof params.isAdmin !== 'undefined') { - data.isAdmin = params.isAdmin; - count++; - } - - // If no update parameters provided, return true - if (count === 0) { - return true; - } + if (Object.keys(data).length <= 2) return false; return boolApiWrapper('/api/admin/update', data); } /** - * Delete user (admin only) - * @param token - Authentication token - * @param username - Username - * @returns true if deletion successful, false otherwise + * Delete a user (admin only). + * + * @param token - The auth token (must belong to an admin user) + * @param username - The username to delete + * @returns `true` on success, `false` on failure */ -export async function deleteItem(token: string, username: string): Promise { +export async function del(token: string, username: string): Promise { return boolApiWrapper('/api/admin/delete', { token, username }); } diff --git a/frontend/src/api/calendar.ts b/frontend/src/api/calendar.ts index a864a85..0491384 100644 --- a/frontend/src/api/calendar.ts +++ b/frontend/src/api/calendar.ts @@ -1,141 +1,172 @@ -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; -// Calendar event interface -interface CalendarEvent { - uuid: string - belongTo: string - title: string - description: string - eventDateTimeStart: string - eventDateTimeEnd: string - loopRules: string - timezoneOffset: number - lastChange: string +/** A raw calendar row as returned by the API (positional from SQL columns) */ +export type CalendarRow = [ + uuid: string, + belongTo: string, + title: string, + description: string, + lastChange: string, + eventDateTimeStart: number, + eventDateTimeEnd: number, + timezoneOffset: number, + loopRules: string, + loopDateTimeStart: number, + loopDateTimeEnd: number, +]; + +/** Serialized description object stored in the `description` column */ +export interface CalendarDescription { + description: string; + color: string; } -// Description object interface -interface DescriptionObject { - description: string - color: string -} +/** Default color used when deserialization fails */ +const DEFAULT_COLOR = '#3388ff'; /** - * Serialize calendar description - * @param description - Description text - * @param color - Color value - * @returns JSON string + * Serialize a calendar description and color into a JSON string + * for storage in the `description` column. + * + * @param description - The description text + * @param color - The color string (e.g. `#ff0000`) + * @returns The JSON-serialized string */ export function serializeDescription(description: string, color: string): string { - const sobj: DescriptionObject = { - description, - color - } - return JSON.stringify(sobj) + return JSON.stringify({ description, color }); } /** - * Deserialize calendar description - * @param str - JSON string - * @returns Description object + * Deserialize a JSON description string back into an object. + * Returns a default object on parse errors. + * + * @param str - The JSON string to deserialize + * @returns The parsed {@link CalendarDescription} object */ -export function deserializeDescription(str: string): DescriptionObject { +export function deserializeDescription(str: string): CalendarDescription { try { - return JSON.parse(str) as DescriptionObject - } catch (err) { - return { - description: "", - color: "#000000" // DefaultColor - } + return JSON.parse(str) as CalendarDescription; + } catch { + return { description: '', color: DEFAULT_COLOR }; } } /** - * Get full calendar events within date range - * @param token - Authentication token - * @param startDateTime - Start datetime - * @param endDateTime - End datetime - * @returns Array of calendar events or null if operation failed + * Fetch all calendar events within the given time range for the current user. + * + * @param token - The auth token + * @param startDateTime - Start of the time range (unix timestamp) + * @param endDateTime - End of the time range (unix timestamp) + * @returns An array of {@link CalendarRow} on success, or `undefined` on failure */ -export async function getFull(token: string, startDateTime: string, endDateTime: string): Promise { - return apiWrapper('/api/calendar/getFull', { token, startDateTime, endDateTime }); +export async function getFull(token: string, startDateTime: number, endDateTime: number): Promise { + return apiWrapper('/api/calendar/getFull', { + token, + startDateTime, + endDateTime, + }); } /** - * Get calendar event detail by UUID - * @param token - Authentication token - * @param uuid - Event UUID - * @returns Calendar event or null if operation failed + * Fetch the full detail of a single calendar event by UUID. + * + * @param token - The auth token + * @param uuid - The event UUID + * @returns A single {@link CalendarRow} on success, or `undefined` on failure */ -export async function getDetail(token: string, uuid: string): Promise { - return apiWrapper('/api/calendar/getDetail', { token, uuid }); +export async function getDetail(token: string, uuid: string): Promise { + return apiWrapper('/api/calendar/getDetail', { + token, + uuid, + }); } /** - * Update calendar event - * @param token - Authentication token - * @param uuid - Event UUID - * @param params - Update parameters (partial) - * @returns Updated calendar event or null if operation failed + * Update an existing calendar event. Only the fields provided will be updated. + * + * @param token - The auth token + * @param uuid - The event UUID + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @param belongTo - (optional) Collection UUID this event belongs to + * @param title - (optional) Event title + * @param description - (optional) Event description (serialized JSON) + * @param eventDateTimeStart - (optional) Start time (unix timestamp) + * @param eventDateTimeEnd - (optional) End time (unix timestamp) + * @param loopRules - (optional) Loop rules string + * @param timezoneOffset - (optional) Timezone offset in minutes + * @returns The new `lastChange` value on success, or `undefined` on failure */ export async function update( token: string, uuid: string, - params: { - belongTo?: string - title?: string - description?: string - eventDateTimeStart?: string - eventDateTimeEnd?: string - loopRules?: string - timezoneOffset?: number - lastChange: string - } -): Promise { - const data: any = { - token, - uuid, - lastChange: params.lastChange - }; + lastChange: string, + belongTo?: string, + title?: string, + description?: string, + eventDateTimeStart?: number, + eventDateTimeEnd?: number, + loopRules?: string, + timezoneOffset?: number, +): Promise { + const data: Record = { token, uuid, lastChange }; + if (belongTo !== undefined) data.belongTo = belongTo; + if (title !== undefined) data.title = title; + if (description !== undefined) data.description = description; + if (eventDateTimeStart !== undefined) data.eventDateTimeStart = eventDateTimeStart; + if (eventDateTimeEnd !== undefined) data.eventDateTimeEnd = eventDateTimeEnd; + if (loopRules !== undefined) data.loopRules = loopRules; + if (timezoneOffset !== undefined) data.timezoneOffset = timezoneOffset; - if (params.belongTo !== undefined) data.belongTo = params.belongTo; - if (params.title !== undefined) data.title = params.title; - if (params.description !== undefined) data.description = params.description; - if (params.eventDateTimeStart !== undefined) data.eventDateTimeStart = params.eventDateTimeStart; - if (params.eventDateTimeEnd !== undefined) data.eventDateTimeEnd = params.eventDateTimeEnd; - if (params.loopRules !== undefined) data.loopRules = params.loopRules; - if (params.timezoneOffset !== undefined) data.timezoneOffset = params.timezoneOffset; - - return apiWrapper('/api/calendar/update', data); + return apiWrapper('/api/calendar/update', data); } /** - * Add new calendar event - * @param token - Authentication token - * @param params - Event parameters - * @returns Created calendar event or null if operation failed + * Create a new calendar event. + * + * @param token - The auth token + * @param belongTo - Collection UUID this event belongs to + * @param title - Event title + * @param description - Event description (serialized JSON) + * @param eventDateTimeStart - Start time (unix timestamp) + * @param eventDateTimeEnd - End time (unix timestamp) + * @param loopRules - Loop rules string + * @param timezoneOffset - Timezone offset in minutes + * @returns The new event UUID on success, or `undefined` on failure */ export async function add( token: string, - params: { - belongTo: string - title: string - description: string - eventDateTimeStart: string - eventDateTimeEnd: string - loopRules: string - timezoneOffset: number - } -): Promise { - return apiWrapper('/api/calendar/add', { token, ...params }); + belongTo: string, + title: string, + description: string, + eventDateTimeStart: number, + eventDateTimeEnd: number, + loopRules: string, + timezoneOffset: number, +): Promise { + return apiWrapper('/api/calendar/add', { + token, + belongTo, + title, + description, + eventDateTimeStart, + eventDateTimeEnd, + loopRules, + timezoneOffset, + }); } /** - * Delete calendar event - * @param token - Authentication token - * @param uuid - Event UUID - * @param lastChange - Last change timestamp - * @returns true if deletion successful, false otherwise + * Delete a calendar event. + * + * @param token - The auth token + * @param uuid - The event UUID + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns `true` on success, `false` on failure */ -export async function deleteEvent(token: string, uuid: string, lastChange: string): Promise { - return boolApiWrapper('/api/calendar/delete', { token, uuid, lastChange }); +export async function del(token: string, uuid: string, lastChange: string): Promise { + return boolApiWrapper('/api/calendar/delete', { + token, + uuid, + lastChange, + }); } diff --git a/frontend/src/api/collection.ts b/frontend/src/api/collection.ts index 0b4e1c4..65ef375 100644 --- a/frontend/src/api/collection.ts +++ b/frontend/src/api/collection.ts @@ -1,126 +1,119 @@ -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; -type Uuid = string; - -// Collection item interface -interface CollectionItem { - uuid: Uuid - name: string - lastChange: string -} - -// Sharing info interface -interface SharingInfo { - target: string - // Add other sharing-related fields as needed -} - -/** - * Get all owned collections - * @param token - Authentication token - * @returns Array of collection items or null if operation failed - */ -export async function getFullOwn(token: string): Promise { - return apiWrapper('/api/collection/getFullOwn', { token }); -} - -/** - * Get owned collection detail by UUID - * @param token - Authentication token - * @param uuid - Collection UUID - * @returns Collection item or null if operation failed - */ -export async function getDetailOwn(token: string, uuid: Uuid): Promise { - return apiWrapper('/api/collection/getDetailOwn', { token, uuid }); -} - -/** - * Add new owned collection - * @param token - Authentication token - * @param name - Collection name - * @returns Created collection item or null if operation failed - */ -export async function addOwn(token: string, name: string): Promise { - return apiWrapper('/api/collection/addOwn', { token, name }); -} - -/** - * Update owned collection - * @param token - Authentication token - * @param uuid - Collection UUID - * @param name - New name - * @param lastChange - Last change timestamp - * @returns Updated collection item or null if operation failed - */ -export async function updateOwn( - token: string, - uuid: Uuid, +/** A raw collection row as returned by the API (positional from SQL columns) */ +export type CollectionRow = [ + uuid: string, name: string, - lastChange: string -): Promise { - return apiWrapper('/api/collection/updateOwn', { token, uuid, name, lastChange }); + lastChange: string, +]; + +/** A raw shared collection row as returned by the API */ +export type SharedCollectionRow = [ + uuid: string, + name: string, + user: string, +]; + +/** + * Fetch all collections owned by the current user. + * + * @param token - The auth token + * @returns An array of {@link CollectionRow} on success, or `undefined` on failure + */ +export async function getFullOwn(token: string): Promise { + return apiWrapper('/api/collection/getFullOwn', { token }); } /** - * Delete owned collection - * @param token - Authentication token - * @param uuid - Collection UUID - * @param lastChange - Last change timestamp - * @returns true if deletion successful, false otherwise + * Fetch the detail of a single owned collection by UUID. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @returns A single {@link CollectionRow} on success, or `undefined` on failure */ -export async function deleteOwn(token: string, uuid: Uuid, lastChange: string): Promise { +export async function getDetailOwn(token: string, uuid: string): Promise { + return apiWrapper('/api/collection/getDetailOwn', { token, uuid }); +} + +/** + * Create a new collection. + * + * @param token - The auth token + * @param name - The collection name + * @returns The new collection UUID on success, or `undefined` on failure + */ +export async function addOwn(token: string, name: string): Promise { + return apiWrapper('/api/collection/addOwn', { token, name }); +} + +/** + * Update an existing collection's name. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @param name - The new collection name + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns The new `lastChange` value on success, or `undefined` on failure + */ +export async function updateOwn(token: string, uuid: string, name: string, lastChange: string): Promise { + return apiWrapper('/api/collection/updateOwn', { token, uuid, name, lastChange }); +} + +/** + * Delete an owned collection. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns `true` on success, `false` on failure + */ +export async function deleteOwn(token: string, uuid: string, lastChange: string): Promise { return boolApiWrapper('/api/collection/deleteOwn', { token, uuid, lastChange }); } /** - * Get sharing information for a collection - * @param token - Authentication token - * @param uuid - Collection UUID - * @returns Array of sharing info or null if operation failed + * Fetch all users this collection is shared with. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @returns An array of target usernames on success, or `undefined` on failure */ -export async function getSharing(token: string, uuid: Uuid): Promise { - return apiWrapper('/api/collection/getSharing', { token, uuid }); +export async function getSharing(token: string, uuid: string): Promise { + return apiWrapper('/api/collection/getSharing', { token, uuid }); } /** - * Delete sharing for a collection - * @param token - Authentication token - * @param uuid - Collection UUID - * @param target - Target user - * @param lastChange - Last change timestamp - * @returns Result data or null if operation failed + * Remove a sharing target from a collection. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @param target - The username to unshare with + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns The new `lastChange` value on success, or `undefined` on failure */ -export async function deleteSharing( - token: string, - uuid: Uuid, - target: string, - lastChange: string -): Promise { - return apiWrapper('/api/collection/deleteSharing', { token, uuid, target, lastChange }); +export async function deleteSharing(token: string, uuid: string, target: string, lastChange: string): Promise { + return apiWrapper('/api/collection/deleteSharing', { token, uuid, target, lastChange }); } /** - * Add sharing for a collection - * @param token - Authentication token - * @param uuid - Collection UUID - * @param target - Target user - * @param lastChange - Last change timestamp - * @returns Result data or null if operation failed + * Add a sharing target to a collection. + * + * @param token - The auth token + * @param uuid - The collection UUID + * @param target - The username to share with + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns The new `lastChange` value on success, or `undefined` on failure */ -export async function addSharing( - token: string, - uuid: Uuid, - target: string, - lastChange: string -): Promise { - return apiWrapper('/api/collection/addSharing', { token, uuid, target, lastChange }); +export async function addSharing(token: string, uuid: string, target: string, lastChange: string): Promise { + return apiWrapper('/api/collection/addSharing', { token, uuid, target, lastChange }); } /** - * Get all shared collections - * @param token - Authentication token - * @returns Array of collection items or null if operation failed + * Fetch all collections shared with the current user. + * + * @param token - The auth token + * @returns An array of {@link SharedCollectionRow} on success, or `undefined` on failure */ -export async function getShared(token: string): Promise { - return apiWrapper('/api/collection/getShared', { token }); +export async function getShared(token: string): Promise { + return apiWrapper('/api/collection/getShared', { token }); } diff --git a/frontend/src/api/common.ts b/frontend/src/api/common.ts index 79f3a34..7b6965c 100644 --- a/frontend/src/api/common.ts +++ b/frontend/src/api/common.ts @@ -1,42 +1,58 @@ -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; // /** -// * Login with salt -// * @param username - Username -// * @param password - Password -// * @returns Token if login successful, undefined otherwise +// * Login via salt-challenge mechanism. +// * +// * First fetches a salt from the server, then computes the salted password hash +// * and sends it to complete login. Returns the auth token on success. +// * +// * @param username - The username +// * @param password - The plaintext password +// * @returns The auth token string on success, or `undefined` on failure // */ // export async function login(username: string, password: string): Promise { -// const salt: string | undefined = await apiWrapper('/api/common/salt', { username }); -// if (typeof salt === 'undefined') return undefined; +// const salt = await apiWrapper('/api/common/salt', { username }); +// if (salt === undefined) return undefined; -// const computedPassword = computePasswordWithSalt(password, salt); -// return apiWrapper('/api/common/login', { username, password: computedPassword }); +// const token = await apiWrapper('/api/common/login', { +// username, +// password: ComputePasswordWithSalt(password, salt.toString()), +// }); + +// return token; // } /** - * Web login - * @param username - Username - * @param password - Password - * @returns Token if login successful, undefined otherwise + * Web login using password hash comparison (no salt challenge). + * + * @param username - The username + * @param password - The plaintext password + * @returns The auth token string on success, or `undefined` on failure */ export async function webLogin(username: string, password: string): Promise { - return apiWrapper('/api/common/webLogin', { username, password }); + const token = await apiWrapper('/api/common/webLogin', { + username, + password, + }); + + return token; } /** - * Logout - * @param token - Authentication token - * @returns true if logout successful, false otherwise if logout failed + * Logout and invalidate the given token on the server. + * + * @param token - The auth token to invalidate + * @returns `true` on success, `false` on failure */ export async function logout(token: string): Promise { return boolApiWrapper('/api/common/logout', { token }); } /** - * Validate token - * @param token - Authentication token - * @returns true if token is valid, false otherwise + * Check whether the given token is still valid. + * + * @param token - The auth token to validate + * @returns `true` if the token is valid, `false` otherwise */ export async function tokenValid(token: string): Promise { return boolApiWrapper('/api/common/tokenValid', { token }); diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 74f2897..6155f8e 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -1,25 +1,30 @@ -// Response interface -interface ApiResponse { - success: boolean, - error: string, - data: T, +/** Response interface for all API calls */ +export interface ApiResponse { + success: boolean; + error: string; + data: T; } +/** + * Generic API wrapper that performs a POST request with URL-encoded form data. + * Returns the `data` field on success, or `undefined` on failure. + * + * @param url - The API endpoint URL + * @param data - Key-value pairs to send as form data + * @returns The response data on success, or `undefined` on failure + */ export async function apiWrapper(url: string, data: Record): Promise { try { - // 自动编码为 key=value&key2=value2 格式 const params = new URLSearchParams(); Object.entries(data).forEach(([key, value]) => { params.append(key, String(value)); }); - // 发起请求 const response = await fetch(url, { method: "POST", mode: "cors", cache: "no-cache", credentials: "same-origin", headers: { - // 明确指定内容类型 'Content-Type': 'application/x-www-form-urlencoded', }, redirect: "follow", @@ -27,16 +32,12 @@ export async function apiWrapper(url: string, data: Record): Pro body: params.toString(), }); - // 检查 HTTP 状态码 (fetch 只有在网络故障时才会 reject,HTTP 404/500 不会) if (!response.ok) { console.error(`HTTP failed: ${response.status}`); } - // 解析 JSON body - // 注意:response.json() 返回的是一个 Promise,所以需要 await const payload = await response.json() as ApiResponse; - // 检查API返回结果 if (payload.success) { return payload.data; } else { @@ -44,14 +45,20 @@ export async function apiWrapper(url: string, data: Record): Pro return undefined; } } catch (error) { - // 统一错误处理 console.error(`Fetch failed: ${error}`); return undefined; } } -export async function boolApiWrapper(url: string, data: Record): Promise { +/** + * Boolean API wrapper. Calls {@link apiWrapper} and returns `true` if the + * response is not `undefined`, otherwise `false`. + * + * @param url - The API endpoint URL + * @param data - Key-value pairs to send as form data + * @returns `true` on success, `false` on failure + */ +export async function boolApiWrapper(url: string, data: Record): Promise { const rv = await apiWrapper(url, data); return rv !== undefined; } - diff --git a/frontend/src/api/profile.ts b/frontend/src/api/profile.ts index 3c4ffbe..40187a5 100644 --- a/frontend/src/api/profile.ts +++ b/frontend/src/api/profile.ts @@ -1,44 +1,51 @@ -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; -// Token info interface -interface TokenInfo { - token: string - // Add other token-related fields as needed -} +/** A raw token row as returned by the API (positional from SQL columns) */ +export type TokenRow = [ + user: string, + token: string, + tokenExpireOn: number, + ua: string, + ip: string, +]; /** - * Check if current user is admin - * @param token - Authentication token - * @returns true if user is admin, false otherwise + * Check whether the current user is an admin. + * + * @param token - The auth token + * @returns `true` if the user is an admin, `false` otherwise */ export async function isAdmin(token: string): Promise { return boolApiWrapper('/api/profile/isAdmin', { token }); } /** - * Change user password - * @param token - Authentication token - * @param password - New password - * @returns true if change successful, false otherwise + * Change the current user's password. + * + * @param token - The auth token + * @param password - The new password (plaintext, will be hashed server-side) + * @returns `true` on success, `false` on failure */ export async function changePassword(token: string, password: string): Promise { return boolApiWrapper('/api/profile/changePassword', { token, password }); } /** - * Get user tokens - * @param token - Authentication token - * @returns Array of token info or undefined if operation failed + * Fetch all tokens associated with the current user. + * + * @param token - The auth token + * @returns An array of {@link TokenRow} on success, or `undefined` on failure */ -export async function getToken(token: string): Promise { - return apiWrapper('/api/profile/getToken', { token }); +export async function getToken(token: string): Promise { + return apiWrapper('/api/profile/getToken', { token }); } /** - * Delete a token - * @param token - Authentication token - * @param deleteToken - Token to delete - * @returns true if deletion successful, false otherwise + * Delete a specific token belonging to the current user. + * + * @param token - The auth token + * @param deleteToken - The token string to delete + * @returns `true` on success, `false` on failure */ export async function deleteToken(token: string, deleteToken: string): Promise { return boolApiWrapper('/api/profile/deleteToken', { token, deleteToken }); diff --git a/frontend/src/api/todo.ts b/frontend/src/api/todo.ts index e6b8ea9..9faeab3 100644 --- a/frontend/src/api/todo.ts +++ b/frontend/src/api/todo.ts @@ -1,56 +1,54 @@ -import axios from 'axios' -import { apiWrapper, boolApiWrapper } from './index' +import { apiWrapper, boolApiWrapper } from './index'; -// Todo item interface -interface TodoItem { - uuid: string - data: any - lastChange: string - // Add other todo-related fields as needed -} - -/** - * Get all todos - * @param token - Authentication token - * @returns Array of todo items or null if operation failed - */ -export async function getFull(token: string): Promise { - return apiWrapper('/api/todo/getFull', { token }); -} - -/** - * Add new todo - * @param token - Authentication token - * @returns Created todo item or null if operation failed - */ -export async function add(token: string): Promise { - return apiWrapper('/api/todo/add', { token }); -} - -/** - * Update todo - * @param token - Authentication token - * @param uuid - Todo UUID - * @param data - Todo data - * @param lastChange - Last change timestamp - * @returns Updated todo item or null if operation failed - */ -export async function update( - token: string, +/** A raw todo row as returned by the API (positional from SQL columns) */ +export type TodoRow = [ uuid: string, - data: any, - lastChange: string -): Promise { - return apiWrapper('/api/todo/update', { token, uuid, data, lastChange }); + belongTo: string, + data: string, + lastChange: string, +]; + +/** + * Fetch all todo items belonging to the current user. + * + * @param token - The auth token + * @returns An array of {@link TodoRow} on success, or `undefined` on failure + */ +export async function getFull(token: string): Promise { + return apiWrapper('/api/todo/getFull', { token }); } /** - * Delete todo - * @param token - Authentication token - * @param uuid - Todo UUID - * @param lastChange - Last change timestamp - * @returns true if deletion successful, false otherwise + * Create a new empty todo item. + * + * @param token - The auth token + * @returns A single {@link TodoRow} of the newly created item on success, or `undefined` on failure */ -export async function deleteTodo(token: string, uuid: string, lastChange: string): Promise { +export async function add(token: string): Promise { + return apiWrapper('/api/todo/add', { token }); +} + +/** + * Update an existing todo item's data. + * + * @param token - The auth token + * @param uuid - The todo UUID + * @param data - The new todo data (serialized string) + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns The new `lastChange` value on success, or `undefined` on failure + */ +export async function update(token: string, uuid: string, data: string, lastChange: string): Promise { + return apiWrapper('/api/todo/update', { token, uuid, data, lastChange }); +} + +/** + * Delete a todo item. + * + * @param token - The auth token + * @param uuid - The todo UUID + * @param lastChange - The last known `lastChange` value (optimistic concurrency) + * @returns `true` on success, `false` on failure + */ +export async function del(token: string, uuid: string, lastChange: string): Promise { return boolApiWrapper('/api/todo/delete', { token, uuid, lastChange }); }