1
0

feat: improve api

This commit is contained in:
2026-06-08 10:45:35 +08:00
parent c4b68400b3
commit ebe7ea5f56
7 changed files with 398 additions and 358 deletions

View File

@@ -1,75 +1,63 @@
import { apiWrapper, boolApiWrapper } from './index' import { apiWrapper, boolApiWrapper } from './index';
// User interface /** A raw admin user row as returned by the API (positional from SQL columns) */
interface User { export type AdminUserRow = [
username: string name: string,
isAdmin: boolean isAdmin: boolean,
// Add other user-related fields as needed ];
}
/** /**
* Get all users (admin only) * Fetch all users (admin only).
* @param token - Authentication token *
* @returns Array of users or null if operation failed * @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<User[] | undefined> { export async function get(token: string): Promise<AdminUserRow[] | undefined> {
return apiWrapper('/api/admin/get', { token }); return apiWrapper<AdminUserRow[]>('/api/admin/get', { token });
} }
/** /**
* Add new user (admin only) * Create a new user (admin only).
* @param token - Authentication token *
* @param username - Username * @param token - The auth token (must belong to an admin user)
* @returns Created user or null if operation failed * @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<User | undefined> { export async function add(token: string, username: string): Promise<AdminUserRow | undefined> {
return apiWrapper('/api/admin/add', { token, username }); return apiWrapper<AdminUserRow>('/api/admin/add', { token, username });
} }
/** /**
* Update user (admin only) * Update an existing user's password and/or admin status (admin only).
* @param token - Authentication token *
* @param username - Username * @param token - The auth token (must belong to an admin user)
* @param params - Update parameters (partial) * @param username - The target user's username
* @returns true if update successful, false otherwise * @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( export async function update(
token: string, token: string,
username: string, username: string,
params: { password?: string,
password?: string isAdmin?: boolean,
isAdmin?: boolean
}
): Promise<boolean> { ): Promise<boolean> {
const data: any = { const data: Record<string, any> = { token, username };
token, if (password !== undefined) data.password = password;
username if (isAdmin !== undefined) data.isAdmin = isAdmin;
};
let count = 0; if (Object.keys(data).length <= 2) return false;
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;
}
return boolApiWrapper('/api/admin/update', data); return boolApiWrapper('/api/admin/update', data);
} }
/** /**
* Delete user (admin only) * Delete a user (admin only).
* @param token - Authentication token *
* @param username - Username * @param token - The auth token (must belong to an admin user)
* @returns true if deletion successful, false otherwise * @param username - The username to delete
* @returns `true` on success, `false` on failure
*/ */
export async function deleteItem(token: string, username: string): Promise<boolean> { export async function del(token: string, username: string): Promise<boolean> {
return boolApiWrapper('/api/admin/delete', { token, username }); return boolApiWrapper('/api/admin/delete', { token, username });
} }

View File

@@ -1,141 +1,172 @@
import { apiWrapper, boolApiWrapper } from './index' import { apiWrapper, boolApiWrapper } from './index';
// Calendar event interface /** A raw calendar row as returned by the API (positional from SQL columns) */
interface CalendarEvent { export type CalendarRow = [
uuid: string uuid: string,
belongTo: string belongTo: string,
title: string title: string,
description: string description: string,
eventDateTimeStart: string lastChange: string,
eventDateTimeEnd: string eventDateTimeStart: number,
loopRules: string eventDateTimeEnd: number,
timezoneOffset: number timezoneOffset: number,
lastChange: string loopRules: string,
loopDateTimeStart: number,
loopDateTimeEnd: number,
];
/** Serialized description object stored in the `description` column */
export interface CalendarDescription {
description: string;
color: string;
} }
// Description object interface /** Default color used when deserialization fails */
interface DescriptionObject { const DEFAULT_COLOR = '#3388ff';
description: string
color: string
}
/** /**
* Serialize calendar description * Serialize a calendar description and color into a JSON string
* @param description - Description text * for storage in the `description` column.
* @param color - Color value *
* @returns JSON string * @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 { export function serializeDescription(description: string, color: string): string {
const sobj: DescriptionObject = { return JSON.stringify({ description, color });
description,
color
}
return JSON.stringify(sobj)
} }
/** /**
* Deserialize calendar description * Deserialize a JSON description string back into an object.
* @param str - JSON string * Returns a default object on parse errors.
* @returns Description object *
* @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 { try {
return JSON.parse(str) as DescriptionObject return JSON.parse(str) as CalendarDescription;
} catch (err) { } catch {
return { return { description: '', color: DEFAULT_COLOR };
description: "",
color: "#000000" // DefaultColor
}
} }
} }
/** /**
* Get full calendar events within date range * Fetch all calendar events within the given time range for the current user.
* @param token - Authentication token *
* @param startDateTime - Start datetime * @param token - The auth token
* @param endDateTime - End datetime * @param startDateTime - Start of the time range (unix timestamp)
* @returns Array of calendar events or null if operation failed * @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<CalendarEvent[] | undefined> { export async function getFull(token: string, startDateTime: number, endDateTime: number): Promise<CalendarRow[] | undefined> {
return apiWrapper('/api/calendar/getFull', { token, startDateTime, endDateTime }); return apiWrapper<CalendarRow[]>('/api/calendar/getFull', {
token,
startDateTime,
endDateTime,
});
} }
/** /**
* Get calendar event detail by UUID * Fetch the full detail of a single calendar event by UUID.
* @param token - Authentication token *
* @param uuid - Event UUID * @param token - The auth token
* @returns Calendar event or null if operation failed * @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<CalendarEvent | undefined> { export async function getDetail(token: string, uuid: string): Promise<CalendarRow | undefined> {
return apiWrapper('/api/calendar/getDetail', { token, uuid }); return apiWrapper<CalendarRow>('/api/calendar/getDetail', {
token,
uuid,
});
} }
/** /**
* Update calendar event * Update an existing calendar event. Only the fields provided will be updated.
* @param token - Authentication token *
* @param uuid - Event UUID * @param token - The auth token
* @param params - Update parameters (partial) * @param uuid - The event UUID
* @returns Updated calendar event or null if operation failed * @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( export async function update(
token: string, token: string,
uuid: string, uuid: string,
params: { lastChange: string,
belongTo?: string belongTo?: string,
title?: string title?: string,
description?: string description?: string,
eventDateTimeStart?: string eventDateTimeStart?: number,
eventDateTimeEnd?: string eventDateTimeEnd?: number,
loopRules?: string loopRules?: string,
timezoneOffset?: number timezoneOffset?: number,
lastChange: string ): Promise<string | undefined> {
} const data: Record<string, any> = { token, uuid, lastChange };
): Promise<CalendarEvent | undefined> { if (belongTo !== undefined) data.belongTo = belongTo;
const data: any = { if (title !== undefined) data.title = title;
token, if (description !== undefined) data.description = description;
uuid, if (eventDateTimeStart !== undefined) data.eventDateTimeStart = eventDateTimeStart;
lastChange: params.lastChange 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; return apiWrapper<string>('/api/calendar/update', data);
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);
} }
/** /**
* Add new calendar event * Create a new calendar event.
* @param token - Authentication token *
* @param params - Event parameters * @param token - The auth token
* @returns Created calendar event or null if operation failed * @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( export async function add(
token: string, token: string,
params: { belongTo: string,
belongTo: string title: string,
title: string description: string,
description: string eventDateTimeStart: number,
eventDateTimeStart: string eventDateTimeEnd: number,
eventDateTimeEnd: string loopRules: string,
loopRules: string timezoneOffset: number,
timezoneOffset: number ): Promise<string | undefined> {
} return apiWrapper<string>('/api/calendar/add', {
): Promise<CalendarEvent | undefined> { token,
return apiWrapper('/api/calendar/add', { token, ...params }); belongTo,
title,
description,
eventDateTimeStart,
eventDateTimeEnd,
loopRules,
timezoneOffset,
});
} }
/** /**
* Delete calendar event * Delete a calendar event.
* @param token - Authentication token *
* @param uuid - Event UUID * @param token - The auth token
* @param lastChange - Last change timestamp * @param uuid - The event UUID
* @returns true if deletion successful, false otherwise * @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<boolean> { export async function del(token: string, uuid: string, lastChange: string): Promise<boolean> {
return boolApiWrapper('/api/calendar/delete', { token, uuid, lastChange }); return boolApiWrapper('/api/calendar/delete', {
token,
uuid,
lastChange,
});
} }

View File

@@ -1,126 +1,119 @@
import { apiWrapper, boolApiWrapper } from './index' import { apiWrapper, boolApiWrapper } from './index';
type Uuid = string; /** A raw collection row as returned by the API (positional from SQL columns) */
export type CollectionRow = [
// Collection item interface uuid: string,
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<CollectionItem[] | undefined> {
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<CollectionItem | undefined> {
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<Uuid | undefined> {
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,
name: string, name: string,
lastChange: string lastChange: string,
): Promise<CollectionItem | undefined> { ];
return apiWrapper('/api/collection/updateOwn', { token, uuid, name, lastChange });
/** 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<CollectionRow[] | undefined> {
return apiWrapper<CollectionRow[]>('/api/collection/getFullOwn', { token });
} }
/** /**
* Delete owned collection * Fetch the detail of a single owned collection by UUID.
* @param token - Authentication token *
* @param uuid - Collection UUID * @param token - The auth token
* @param lastChange - Last change timestamp * @param uuid - The collection UUID
* @returns true if deletion successful, false otherwise * @returns A single {@link CollectionRow} on success, or `undefined` on failure
*/ */
export async function deleteOwn(token: string, uuid: Uuid, lastChange: string): Promise<boolean> { export async function getDetailOwn(token: string, uuid: string): Promise<CollectionRow | undefined> {
return apiWrapper<CollectionRow>('/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<string | undefined> {
return apiWrapper<string>('/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<string | undefined> {
return apiWrapper<string>('/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<boolean> {
return boolApiWrapper('/api/collection/deleteOwn', { token, uuid, lastChange }); return boolApiWrapper('/api/collection/deleteOwn', { token, uuid, lastChange });
} }
/** /**
* Get sharing information for a collection * Fetch all users this collection is shared with.
* @param token - Authentication token *
* @param uuid - Collection UUID * @param token - The auth token
* @returns Array of sharing info or null if operation failed * @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<SharingInfo[] | undefined> { export async function getSharing(token: string, uuid: string): Promise<string[] | undefined> {
return apiWrapper('/api/collection/getSharing', { token, uuid }); return apiWrapper<string[]>('/api/collection/getSharing', { token, uuid });
} }
/** /**
* Delete sharing for a collection * Remove a sharing target from a collection.
* @param token - Authentication token *
* @param uuid - Collection UUID * @param token - The auth token
* @param target - Target user * @param uuid - The collection UUID
* @param lastChange - Last change timestamp * @param target - The username to unshare with
* @returns Result data or null if operation failed * @param lastChange - The last known `lastChange` value (optimistic concurrency)
* @returns The new `lastChange` value on success, or `undefined` on failure
*/ */
export async function deleteSharing( export async function deleteSharing(token: string, uuid: string, target: string, lastChange: string): Promise<string | undefined> {
token: string, return apiWrapper<string>('/api/collection/deleteSharing', { token, uuid, target, lastChange });
uuid: Uuid,
target: string,
lastChange: string
): Promise<any | undefined> {
return apiWrapper('/api/collection/deleteSharing', { token, uuid, target, lastChange });
} }
/** /**
* Add sharing for a collection * Add a sharing target to a collection.
* @param token - Authentication token *
* @param uuid - Collection UUID * @param token - The auth token
* @param target - Target user * @param uuid - The collection UUID
* @param lastChange - Last change timestamp * @param target - The username to share with
* @returns Result data or null if operation failed * @param lastChange - The last known `lastChange` value (optimistic concurrency)
* @returns The new `lastChange` value on success, or `undefined` on failure
*/ */
export async function addSharing( export async function addSharing(token: string, uuid: string, target: string, lastChange: string): Promise<string | undefined> {
token: string, return apiWrapper<string>('/api/collection/addSharing', { token, uuid, target, lastChange });
uuid: Uuid,
target: string,
lastChange: string
): Promise<any | undefined> {
return apiWrapper('/api/collection/addSharing', { token, uuid, target, lastChange });
} }
/** /**
* Get all shared collections * Fetch all collections shared with the current user.
* @param token - Authentication token *
* @returns Array of collection items or null if operation failed * @param token - The auth token
* @returns An array of {@link SharedCollectionRow} on success, or `undefined` on failure
*/ */
export async function getShared(token: string): Promise<CollectionItem[] | undefined> { export async function getShared(token: string): Promise<SharedCollectionRow[] | undefined> {
return apiWrapper('/api/collection/getShared', { token }); return apiWrapper<SharedCollectionRow[]>('/api/collection/getShared', { token });
} }

View File

@@ -1,42 +1,58 @@
import { apiWrapper, boolApiWrapper } from './index' import { apiWrapper, boolApiWrapper } from './index';
// /** // /**
// * Login with salt // * Login via salt-challenge mechanism.
// * @param username - Username // *
// * @param password - Password // * First fetches a salt from the server, then computes the salted password hash
// * @returns Token if login successful, undefined otherwise // * 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<string | undefined> { // export async function login(username: string, password: string): Promise<string | undefined> {
// const salt: string | undefined = await apiWrapper('/api/common/salt', { username }); // const salt = await apiWrapper<number>('/api/common/salt', { username });
// if (typeof salt === 'undefined') return undefined; // if (salt === undefined) return undefined;
// const computedPassword = computePasswordWithSalt(password, salt); // const token = await apiWrapper<string>('/api/common/login', {
// return apiWrapper('/api/common/login', { username, password: computedPassword }); // username,
// password: ComputePasswordWithSalt(password, salt.toString()),
// });
// return token;
// } // }
/** /**
* Web login * Web login using password hash comparison (no salt challenge).
* @param username - Username *
* @param password - Password * @param username - The username
* @returns Token if login successful, undefined otherwise * @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<string | undefined> { export async function webLogin(username: string, password: string): Promise<string | undefined> {
return apiWrapper('/api/common/webLogin', { username, password }); const token = await apiWrapper<string>('/api/common/webLogin', {
username,
password,
});
return token;
} }
/** /**
* Logout * Logout and invalidate the given token on the server.
* @param token - Authentication token *
* @returns true if logout successful, false otherwise if logout failed * @param token - The auth token to invalidate
* @returns `true` on success, `false` on failure
*/ */
export async function logout(token: string): Promise<boolean> { export async function logout(token: string): Promise<boolean> {
return boolApiWrapper('/api/common/logout', { token }); return boolApiWrapper('/api/common/logout', { token });
} }
/** /**
* Validate token * Check whether the given token is still valid.
* @param token - Authentication token *
* @returns true if token is valid, false otherwise * @param token - The auth token to validate
* @returns `true` if the token is valid, `false` otherwise
*/ */
export async function tokenValid(token: string): Promise<boolean> { export async function tokenValid(token: string): Promise<boolean> {
return boolApiWrapper('/api/common/tokenValid', { token }); return boolApiWrapper('/api/common/tokenValid', { token });

View File

@@ -1,25 +1,30 @@
// Response interface /** Response interface for all API calls */
interface ApiResponse<T = any> { export interface ApiResponse<T = any> {
success: boolean, success: boolean;
error: string, error: string;
data: T, 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<T>(url: string, data: Record<string, any>): Promise<T | undefined> { export async function apiWrapper<T>(url: string, data: Record<string, any>): Promise<T | undefined> {
try { try {
// 自动编码为 key=value&key2=value2 格式
const params = new URLSearchParams(); const params = new URLSearchParams();
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
params.append(key, String(value)); params.append(key, String(value));
}); });
// 发起请求
const response = await fetch(url, { const response = await fetch(url, {
method: "POST", method: "POST",
mode: "cors", mode: "cors",
cache: "no-cache", cache: "no-cache",
credentials: "same-origin", credentials: "same-origin",
headers: { headers: {
// 明确指定内容类型
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}, },
redirect: "follow", redirect: "follow",
@@ -27,16 +32,12 @@ export async function apiWrapper<T>(url: string, data: Record<string, any>): Pro
body: params.toString(), body: params.toString(),
}); });
// 检查 HTTP 状态码 (fetch 只有在网络故障时才会 rejectHTTP 404/500 不会)
if (!response.ok) { if (!response.ok) {
console.error(`HTTP failed: ${response.status}`); console.error(`HTTP failed: ${response.status}`);
} }
// 解析 JSON body
// 注意response.json() 返回的是一个 Promise所以需要 await
const payload = await response.json() as ApiResponse<T>; const payload = await response.json() as ApiResponse<T>;
// 检查API返回结果
if (payload.success) { if (payload.success) {
return payload.data; return payload.data;
} else { } else {
@@ -44,14 +45,20 @@ export async function apiWrapper<T>(url: string, data: Record<string, any>): Pro
return undefined; return undefined;
} }
} catch (error) { } catch (error) {
// 统一错误处理
console.error(`Fetch failed: ${error}`); console.error(`Fetch failed: ${error}`);
return undefined; return undefined;
} }
} }
export async function boolApiWrapper<U>(url: string, data: Record<string, any>): Promise<boolean> { /**
* 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<string, any>): Promise<boolean> {
const rv = await apiWrapper<null>(url, data); const rv = await apiWrapper<null>(url, data);
return rv !== undefined; return rv !== undefined;
} }

View File

@@ -1,44 +1,51 @@
import { apiWrapper, boolApiWrapper } from './index' import { apiWrapper, boolApiWrapper } from './index';
// Token info interface /** A raw token row as returned by the API (positional from SQL columns) */
interface TokenInfo { export type TokenRow = [
token: string user: string,
// Add other token-related fields as needed token: string,
} tokenExpireOn: number,
ua: string,
ip: string,
];
/** /**
* Check if current user is admin * Check whether the current user is an admin.
* @param token - Authentication token *
* @returns true if user is admin, false otherwise * @param token - The auth token
* @returns `true` if the user is an admin, `false` otherwise
*/ */
export async function isAdmin(token: string): Promise<boolean> { export async function isAdmin(token: string): Promise<boolean> {
return boolApiWrapper('/api/profile/isAdmin', { token }); return boolApiWrapper('/api/profile/isAdmin', { token });
} }
/** /**
* Change user password * Change the current user's password.
* @param token - Authentication token *
* @param password - New password * @param token - The auth token
* @returns true if change successful, false otherwise * @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<boolean> { export async function changePassword(token: string, password: string): Promise<boolean> {
return boolApiWrapper('/api/profile/changePassword', { token, password }); return boolApiWrapper('/api/profile/changePassword', { token, password });
} }
/** /**
* Get user tokens * Fetch all tokens associated with the current user.
* @param token - Authentication token *
* @returns Array of token info or undefined if operation failed * @param token - The auth token
* @returns An array of {@link TokenRow} on success, or `undefined` on failure
*/ */
export async function getToken(token: string): Promise<TokenInfo[] | undefined> { export async function getToken(token: string): Promise<TokenRow[] | undefined> {
return apiWrapper('/api/profile/getToken', { token }); return apiWrapper<TokenRow[]>('/api/profile/getToken', { token });
} }
/** /**
* Delete a token * Delete a specific token belonging to the current user.
* @param token - Authentication token *
* @param deleteToken - Token to delete * @param token - The auth token
* @returns true if deletion successful, false otherwise * @param deleteToken - The token string to delete
* @returns `true` on success, `false` on failure
*/ */
export async function deleteToken(token: string, deleteToken: string): Promise<boolean> { export async function deleteToken(token: string, deleteToken: string): Promise<boolean> {
return boolApiWrapper('/api/profile/deleteToken', { token, deleteToken }); return boolApiWrapper('/api/profile/deleteToken', { token, deleteToken });

View File

@@ -1,56 +1,54 @@
import axios from 'axios' import { apiWrapper, boolApiWrapper } from './index';
import { apiWrapper, boolApiWrapper } from './index'
// Todo item interface /** A raw todo row as returned by the API (positional from SQL columns) */
interface TodoItem { export type TodoRow = [
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<TodoItem[] | undefined> {
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<TodoItem | undefined> {
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,
uuid: string, uuid: string,
data: any, belongTo: string,
lastChange: string data: string,
): Promise<TodoItem | undefined> { lastChange: string,
return apiWrapper('/api/todo/update', { token, uuid, data, lastChange }); ];
/**
* 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<TodoRow[] | undefined> {
return apiWrapper<TodoRow[]>('/api/todo/getFull', { token });
} }
/** /**
* Delete todo * Create a new empty todo item.
* @param token - Authentication token *
* @param uuid - Todo UUID * @param token - The auth token
* @param lastChange - Last change timestamp * @returns A single {@link TodoRow} of the newly created item on success, or `undefined` on failure
* @returns true if deletion successful, false otherwise
*/ */
export async function deleteTodo(token: string, uuid: string, lastChange: string): Promise<boolean> { export async function add(token: string): Promise<TodoRow | undefined> {
return apiWrapper<TodoRow>('/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<string | undefined> {
return apiWrapper<string>('/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<boolean> {
return boolApiWrapper('/api/todo/delete', { token, uuid, lastChange }); return boolApiWrapper('/api/todo/delete', { token, uuid, lastChange });
} }