# API Reference Base URL in local development: `http://localhost:8080`. Frontend usually calls paths like `/api/resources`; uploaded files are available under `/uploads/...`. ## Response style Most responses are JSON. Errors use plain text via `http.Error`, for example `unauthorized` or `bad json`. ## Health ### `GET /healthz` Returns plain text: ```text ok ``` ### `GET /health` Same as `/healthz`. ## Public categories/resources ### `GET /api/categories` Query/header language behavior: - `?lang=zh-CN` returns Simplified Chinese name when available. - `?lang=en` returns English name when available. - otherwise Traditional Chinese is preferred. Response: ```json [ { "id": 1, "slug": "project-ppt", "name": "項目資料(PPT)", "description": "ARK 項目介紹、簡報與對外展示資料", "iconKey": "folder", "sortOrder": 1, "updatedAt": "2026-01-01T00:00:00Z" } ] ``` ### `GET /api/resources` Returns paginated public resources. Query params: | Param | Meaning | | --- | --- | | `page` | page number, default 1 | | `limit` | page size, default 20, max 100 | | `q` | search title/description/tag name | | `type` | resource type; `all` means no filter | | `language` | exact resource language filter | | `category` | category slug | | `tag` | tag slug or tag name, case-insensitive | | `sort` | `latest` default, `published`, `recommended`, `popular` | | `lang` | category display language, e.g. `en`, `zh-CN` | Response: ```json { "items": [ { "id": "uuid", "title": "ARK 項目介紹簡報(示例)", "description": "適合線下宣講與新人培訓。", "type": "ppt", "language": "zh-TW", "categoryId": 1, "categorySlug": "project-ppt", "categoryName": "項目資料(PPT)", "coverImage": "/uploads/placeholder-cover.svg", "fileUrl": "/uploads/placeholder-cover.svg", "isDownloadable": true, "isRecommended": true, "publishedAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "tags": ["官方推薦"] } ], "page": 1, "limit": 20, "total": 1 } ``` ### `GET /api/resources/recommended?limit=12` Returns recommended resources: ```json { "items": [] } ``` Max limit is 50. ### `GET /api/resources/latest?limit=12` Returns latest resources: ```json { "items": [] } ``` ### `GET /api/resources/popular?limit=12` Returns resources ordered by download + favorite + share count: ```json { "items": [] } ``` ### `GET /api/resources/{id}` Returns one published public resource. `{id}` must be a UUID. ### `GET /api/resources/{id}/related` Returns up to 8 resources from the same category: ```json { "items": [] } ``` ## Public activity endpoints ### `POST /api/search-log` Request: ```json { "query": "ark" } ``` Response: ```json { "ok": true } ``` ### `POST /api/resources/{id}/view` Increments view count. Response: ```json { "ok": true } ``` ### `POST /api/resources/{id}/download` Increments download count. ### `POST /api/resources/{id}/share` Increments share count. ### `POST /api/resources/{id}/favorite` Request: ```json { "add": true } ``` - `add: true` increments favorite count. - `add: false` decrements favorite count but not below zero. ## Wallet auth Wallet auth is for normal users, not admin users. ### `POST /api/auth/wallet/nonce` Request: ```json { "address": "0x0000000000000000000000000000000000000000" } ``` Response: ```json { "nonce": "hex-code", "message": "ARK Database — wallet sign-in\n\nWallet: 0x...\nOne-time code: ..." } ``` The frontend asks the wallet to sign `message`. ### `POST /api/auth/wallet/verify` Request: ```json { "address": "0x0000000000000000000000000000000000000000", "message": "same message from nonce endpoint", "signature": "0x..." } ``` Response: ```json { "token": "jwt", "wallet": "0xChecksumAddress" } ``` ### `GET /api/auth/wallet/me` Requires header: ```http Authorization: Bearer ``` Response: ```json { "wallet": "0xChecksumAddress", "role": "user" } ``` ## Admin auth ### `POST /api/admin/login` Request: ```json { "email": "admin@ark.local", "password": "admin123" } ``` Response: ```json { "token": "admin-jwt" } ``` Use this token for admin endpoints: ```http Authorization: Bearer ``` ## Admin endpoints All endpoints below require admin JWT. ### `GET /api/admin/dashboard` Response includes counts and hot resources: ```json { "totalResources": 10, "published": 8, "todayNew": 1, "totalViews": 100, "totalDownloads": 20, "totalFavorites": 5, "totalShares": 3, "hotResources": [] } ``` ### `GET /api/admin/search-logs?limit=200` Max limit is 500. Response: ```json { "items": [{ "id": 1, "query": "ark", "createdAt": "2026-01-01T00:00:00Z" }] } ``` ### `GET /api/admin/resources?page=1&limit=20` Lists all resources, including drafts/private resources. ### `GET /api/admin/resources/{id}` Gets one resource by UUID. ### `POST /api/admin/resources` Creates a resource. Example request body: ```json { "title": "Example", "description": "Short text", "type": "ppt", "language": "zh-TW", "categoryId": 1, "coverImage": "/uploads/example.svg", "fileUrl": "/uploads/example.pdf", "previewUrl": "", "externalUrl": "", "bodyText": "", "badgeLabel": "新人必看", "isPublic": true, "isDownloadable": true, "isRecommended": false, "sortOrder": 0, "status": "draft", "tags": ["教程"] } ``` ### `PUT /api/admin/resources/{id}` Updates a resource. Body shape is the same as create. ### `DELETE /api/admin/resources/{id}` Deletes a resource. Response: ```json { "ok": true } ``` ### `POST /api/admin/upload` Multipart upload with field name `file`. Response for local storage: ```json { "url": "/uploads/generated-name.ext", "filename": "generated-name.ext", "storage": "local" } ``` Response for S3: ```json { "url": "https://...", "filename": "generated-name.ext", "storage": "s3" } ``` ### `GET /api/admin/categories` Same handler as public categories; returns visible categories.