cosmoe.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /* eslint-disable */
  2. /**
  3. * Cosmoe API Client
  4. * API Documentation: https://cos.world/api/v1/api_docs.html
  5. * Base URL: http://cos.moe/api/v1/
  6. */
  7. interface ApiResponse<T = any> {
  8. code: number;
  9. msg?: string;
  10. data: T;
  11. }
  12. interface TokenResponse {
  13. user_id: number;
  14. token: string;
  15. }
  16. interface Event {
  17. id: string; // Changed from number to string based on API response
  18. name: string;
  19. description: string;
  20. event_date: string; // Changed from 'date'
  21. cover_image_url: string; // Changed from 'cover_image'
  22. description_brief: string; // Changed from 'summary'
  23. }
  24. interface TimeSlot {
  25. range: string; // e.g. "13:30-14:30"
  26. price: string; // price as string
  27. capacity: number;
  28. remaining: number;
  29. }
  30. interface EventDetail {
  31. id: number;
  32. name: string;
  33. description: string;
  34. event_date: string; // Changed from 'date'
  35. time_slots_with_prices: string; // Raw string format
  36. cover_image_url: string; // Changed from 'cover_image'
  37. detail_image_urls: string; // Changed from 'gallery'
  38. is_listed: number;
  39. login_required: number;
  40. allow_coupons: number;
  41. created_at: string;
  42. slots: TimeSlot[];
  43. gallery: string[]; // Still exists as array of URLs
  44. }
  45. interface UserInfo {
  46. id: number;
  47. username: string;
  48. email: string;
  49. user_identity: string;
  50. role: string;
  51. is_active: number;
  52. created_at: string;
  53. }
  54. interface Statistics {
  55. total: number;
  56. completed: number;
  57. pending: number;
  58. }
  59. interface Coupon {
  60. id: number;
  61. code: string;
  62. type: string; // 'percentage' or 'fixed'
  63. value: number;
  64. valid_to: string;
  65. }
  66. interface UserProfile {
  67. user_info: UserInfo;
  68. statistics: Statistics;
  69. }
  70. interface BookingRequest {
  71. event_id: number | string;
  72. time_slot: string;
  73. user_note?: string;
  74. payment_order_id?: string;
  75. coupon_code?: string;
  76. }
  77. // Keeping ProfileData for backward compatibility
  78. interface ProfileData {
  79. user_info: UserInfo;
  80. statistics: Statistics;
  81. }
  82. interface Booking {
  83. id: number;
  84. status: string;
  85. booking_date: string;
  86. time_slot: string;
  87. final_price: string;
  88. payment_order_id: string;
  89. notes_by_user: string;
  90. created_at: string;
  91. event_name: string;
  92. }
  93. interface BookingRequest {
  94. event_id: number | string; // Changed to accept both string and number based on API response
  95. time_slot: string;
  96. user_note?: string;
  97. }
  98. export class CosmoeClient {
  99. private baseUrl = "https://cos.world/api/v1";
  100. private userId?: number;
  101. private token?: string;
  102. /**
  103. * Get authentication token
  104. * @param username User's username
  105. * @param password User's password
  106. * @returns Token response with user_id and token
  107. */
  108. async getToken(username: string, password: string): Promise<ApiResponse<TokenResponse>> {
  109. const formData = new FormData();
  110. formData.append("username", username);
  111. formData.append("password", password);
  112. const response = await fetch(`${this.baseUrl}/CreatToken.php`, {
  113. method: "POST",
  114. body: formData,
  115. });
  116. const result: ApiResponse<TokenResponse> = await response.json();
  117. if (result.code === 200 && result.data) {
  118. this.userId = result.data.user_id;
  119. this.token = result.data.token;
  120. }
  121. return result;
  122. }
  123. /**
  124. * Set user credentials manually
  125. * @param userId User ID
  126. * @param token Authentication token
  127. */
  128. setCredentials(userId: number, token: string): void {
  129. this.userId = userId;
  130. this.token = token;
  131. }
  132. /**
  133. * Get current user credentials
  134. * @returns Current userId and token if authenticated
  135. */
  136. getCredentials(): { userId: number, token: string } | null {
  137. if (this.isAuthenticated()) {
  138. return {
  139. userId: this.userId!,
  140. token: this.token!
  141. };
  142. }
  143. return null;
  144. }
  145. /**
  146. * Check if user is authenticated
  147. */
  148. isAuthenticated(): boolean {
  149. return !!this.userId && !!this.token;
  150. }
  151. /**
  152. * Get events list
  153. * @returns List of events
  154. */
  155. async getEvents(): Promise<ApiResponse<Event[]>> {
  156. const params = new URLSearchParams({
  157. action: "get_events",
  158. });
  159. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  160. return await response.json() as ApiResponse<Event[]>;
  161. }
  162. /**
  163. * Get event details
  164. * @param eventId Event ID to get details for
  165. * @returns Event details
  166. */
  167. async getEventDetail(eventId: number | string): Promise<ApiResponse<EventDetail>> {
  168. const params = new URLSearchParams({
  169. action: "get_event_detail",
  170. event_id: String(eventId),
  171. });
  172. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  173. return await response.json() as ApiResponse<EventDetail>;
  174. }
  175. /**
  176. * Get user profile and statistics
  177. * Requires authentication
  178. * @returns User profile and statistics
  179. */
  180. async getProfile(): Promise<ApiResponse<ProfileData>> {
  181. if (!this.isAuthenticated()) {
  182. throw new Error("Authentication required");
  183. }
  184. const params = new URLSearchParams({
  185. action: "get_profile",
  186. user_id: this.userId!.toString(),
  187. token: this.token!,
  188. });
  189. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  190. return await response.json() as ApiResponse<ProfileData>;
  191. }
  192. /**
  193. * Get user's booking history
  194. * Requires authentication
  195. * @returns User's booking history
  196. */
  197. async getMyBookings(): Promise<ApiResponse<Booking[]>> {
  198. if (!this.isAuthenticated()) {
  199. throw new Error("Authentication required");
  200. }
  201. const params = new URLSearchParams({
  202. action: "get_my_bookings",
  203. user_id: this.userId!.toString(),
  204. token: this.token!,
  205. });
  206. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  207. return await response.json() as ApiResponse<Booking[]>;
  208. }
  209. /**
  210. * Book an event
  211. * Requires authentication
  212. * @param bookingRequest Booking details
  213. * @returns Booking result
  214. */
  215. async bookEvent(bookingRequest: BookingRequest): Promise<ApiResponse<any>> {
  216. if (!this.isAuthenticated()) {
  217. throw new Error("Authentication required");
  218. }
  219. const formData = new FormData();
  220. formData.append("action", "book_event");
  221. formData.append("user_id", this.userId!.toString());
  222. formData.append("token", this.token!);
  223. formData.append("event_id", String(bookingRequest.event_id));
  224. formData.append("time_slot", bookingRequest.time_slot);
  225. formData.append("coupon_code", bookingRequest.coupon_code || "");
  226. if (bookingRequest.user_note) {
  227. formData.append("user_note", bookingRequest.user_note);
  228. }
  229. const response = await fetch(`${this.baseUrl}/api.php`, {
  230. method: "POST",
  231. body: formData,
  232. });
  233. return await response.json() as ApiResponse<any>;
  234. }
  235. /**
  236. * Change user password
  237. * Requires authentication
  238. * @param currentPassword Current password
  239. * @param newPassword New password (at least 6 characters)
  240. * @returns Password change result
  241. */
  242. async changePassword(currentPassword: string, newPassword: string): Promise<ApiResponse<any>> {
  243. if (!this.isAuthenticated()) {
  244. throw new Error("Authentication required");
  245. }
  246. if (newPassword.length < 6) {
  247. throw new Error("New password must be at least 6 characters");
  248. }
  249. const formData = new FormData();
  250. formData.append("action", "change_password");
  251. formData.append("user_id", this.userId!.toString());
  252. formData.append("token", this.token!);
  253. formData.append("current_password", currentPassword);
  254. formData.append("new_password", newPassword);
  255. const response = await fetch(`${this.baseUrl}/api.php`, {
  256. method: "POST",
  257. body: formData,
  258. });
  259. return await response.json() as ApiResponse<any>;
  260. }
  261. /**
  262. * Get user profile with updated structure
  263. * Requires authentication
  264. * @returns User profile and statistics
  265. */
  266. async getUserProfile(): Promise<ApiResponse<UserProfile>> {
  267. if (!this.isAuthenticated()) {
  268. throw new Error("Authentication required");
  269. }
  270. const params = new URLSearchParams({
  271. action: "get_profile",
  272. user_id: this.userId!.toString(),
  273. token: this.token!,
  274. });
  275. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  276. return await response.json() as ApiResponse<UserProfile>;
  277. }
  278. /**
  279. * Get available coupons for an event
  280. * Requires authentication
  281. * @param eventId Event ID to get coupons for
  282. * @returns List of available coupons
  283. */
  284. async getAvailableCoupons(eventId: number | string): Promise<ApiResponse<Coupon[]>> {
  285. if (!this.isAuthenticated()) {
  286. throw new Error("Authentication required");
  287. }
  288. const params = new URLSearchParams({
  289. action: "get_available_coupons",
  290. user_id: this.userId!.toString(),
  291. token: this.token!,
  292. event_id: String(eventId),
  293. });
  294. const response = await fetch(`${this.baseUrl}/api.php?${params}`);
  295. return await response.json() as ApiResponse<Coupon[]>;
  296. }
  297. /**
  298. * Update booking with payment order ID
  299. * Requires authentication
  300. * @param bookingId Booking ID to update
  301. * @param paymentOrderId New payment order ID
  302. * @returns Update result
  303. */
  304. async updatePaymentOrder(bookingId: number, paymentOrderId: string): Promise<ApiResponse<any>> {
  305. if (!this.isAuthenticated()) {
  306. throw new Error("Authentication required");
  307. }
  308. const formData = new FormData();
  309. formData.append("action", "update_payment_order");
  310. formData.append("user_id", this.userId!.toString());
  311. formData.append("token", this.token!);
  312. formData.append("booking_id", bookingId.toString());
  313. formData.append("payment_order_id", paymentOrderId);
  314. const response = await fetch(`${this.baseUrl}/api.php`, {
  315. method: "POST",
  316. body: formData,
  317. });
  318. return await response.json() as ApiResponse<any>;
  319. }
  320. /**
  321. * Update booking note
  322. * Requires authentication
  323. * @param bookingId Booking ID to update
  324. * @param note New note
  325. * @returns Update result
  326. */
  327. async updateBookingNote(bookingId: number, note: string): Promise<ApiResponse<any>> {
  328. if (!this.isAuthenticated()) {
  329. throw new Error("Authentication required");
  330. }
  331. const formData = new FormData();
  332. formData.append("action", "update_booking_note");
  333. formData.append("user_id", this.userId!.toString());
  334. formData.append("token", this.token!);
  335. formData.append("booking_id", bookingId.toString());
  336. formData.append("note", note);
  337. const response = await fetch(`${this.baseUrl}/api.php`, {
  338. method: "POST",
  339. body: formData,
  340. });
  341. return await response.json() as ApiResponse<any>;
  342. }
  343. /**
  344. * Cancel a booking
  345. * Requires authentication
  346. * @param bookingId Booking ID to cancel
  347. * @returns Cancel result
  348. */
  349. async cancelBooking(bookingId: number): Promise<ApiResponse<any>> {
  350. if (!this.isAuthenticated()) {
  351. throw new Error("Authentication required");
  352. }
  353. const formData = new FormData();
  354. formData.append("action", "cancel_booking");
  355. formData.append("user_id", this.userId!.toString());
  356. formData.append("token", this.token!);
  357. formData.append("booking_id", bookingId.toString());
  358. const response = await fetch(`${this.baseUrl}/api.php`, {
  359. method: "POST",
  360. body: formData,
  361. });
  362. return await response.json() as ApiResponse<any>;
  363. }
  364. /**
  365. * Self reschedule a booking
  366. * Requires authentication
  367. * @param bookingId Booking ID to reschedule
  368. * @param newTimeSlot New time slot for the booking
  369. * @returns Reschedule result
  370. */
  371. async selfReschedule(bookingId: number, newTimeSlot: string): Promise<ApiResponse<any>> {
  372. if (!this.isAuthenticated()) {
  373. throw new Error("Authentication required");
  374. }
  375. const formData = new FormData();
  376. formData.append("action", "self_reschedule");
  377. formData.append("user_id", this.userId!.toString());
  378. formData.append("token", this.token!);
  379. formData.append("booking_id", bookingId.toString());
  380. formData.append("new_time_slot", newTimeSlot);
  381. const response = await fetch(`${this.baseUrl}/api.php`, {
  382. method: "POST",
  383. body: formData,
  384. });
  385. return await response.json() as ApiResponse<any>;
  386. }
  387. /**
  388. * Self transfer a booking to another user
  389. * Requires authentication
  390. * @param bookingId Booking ID to transfer
  391. * @param recipientUsername Username of the recipient
  392. * @returns Transfer result
  393. */
  394. async selfTransfer(bookingId: number, recipientUsername: string): Promise<ApiResponse<any>> {
  395. if (!this.isAuthenticated()) {
  396. throw new Error("Authentication required");
  397. }
  398. const formData = new FormData();
  399. formData.append("action", "self_transfer");
  400. formData.append("user_id", this.userId!.toString());
  401. formData.append("token", this.token!);
  402. formData.append("booking_id", bookingId.toString());
  403. formData.append("recipient_username", recipientUsername);
  404. const response = await fetch(`${this.baseUrl}/api.php`, {
  405. method: "POST",
  406. body: formData,
  407. });
  408. return await response.json() as ApiResponse<any>;
  409. }
  410. /**
  411. * Register a new user
  412. * @param key Permission key (contact admin for access)
  413. * @param username Username (2-10 characters, alphanumeric and underscore only)
  414. * @param email Email address
  415. * @param password Password (at least 6 characters)
  416. * @param userIdentity User identity (coser/photographer/other, default photographer)
  417. * @returns Registration result
  418. */
  419. async register(key: string, username: string, email: string, password: string, userIdentity: string = "photographer"): Promise<ApiResponse<{ user_id: number, username: string }>> {
  420. const formData = new FormData();
  421. formData.append("action", "register");
  422. formData.append("key", key);
  423. formData.append("username", username);
  424. formData.append("email", email);
  425. formData.append("password", password);
  426. formData.append("user_identity", userIdentity);
  427. const response = await fetch(`${this.baseUrl}/api.php`, {
  428. method: "POST",
  429. body: formData,
  430. });
  431. return await response.json() as ApiResponse<{ user_id: number, username: string }>;
  432. }
  433. }