···20202121- **Access**: Navigate to `/admin.html` or click the "Admin Dashboard" link on the main page
2222- **Features**:
2323- - View all levels with pagination
2424- - Search levels by name, author, or ID
2525- - Edit level properties (name, author, sun, water status, difficulty, statistics)
2626- - Delete levels (including related files and database entries)
2323+ - View all levels with pagination
2424+ - Search levels by name, author, or ID
2525+ - Edit level properties (name, author, sun, water status, difficulty, statistics)
2626+ - Delete levels (including related files and database entries)
2727 > Authentication: The admin UI supports optional GitHub OAuth. If `USE_GITHUB_AUTH=true`, users must sign in with GitHub and be included in `GITHUB_ALLOWED_USERS` to access admin endpoints. If `USE_GITHUB_AUTH=false`, the admin endpoints are not protected (not recommended in production).
2828 > The admin dashboard also supports one-time-token links for a single edit/delete action:
2929- - Edit: `/admin.html?token=...&action=edit&level=123`
3030- - Delete: `/admin.html?token=...&action=delete&level=123`
2929+ - Edit: `/admin.html?token=...&action=edit&level=123`
3030+ - Delete: `/admin.html?token=...&action=delete&level=123`
3131 After a successful token-based edit/delete, the page attempts to call `window.close()` (some browsers only allow this for windows opened by script).
32323333## Getting Started
···8383- **URL:** `/api/levels`
8484- **Method:** `POST`
8585- **Content Types:**
8686- - `application/octet-stream`
8686+ - `application/octet-stream`
8787- **URL Params:** None
8888- **Query Params:** (for octet-stream)
8989- - `author`: Author name
9090- - `turnstileResponse`: Captcha verification token (if enabled)
8989+ - `author`: Author name
9090+ - `turnstileResponse`: Captcha verification token (if enabled)
9191- **Notes:** Only IZL3 is supported (v2 is deprecated).
9292- **Request Body:** Raw binary level data (`.izl3`), sent as the request body.
9393- **Success Response:**
9494- - **Code:** 201
9595- - **Content:**
9494+ - **Code:** 201
9595+ - **Content:**
96969797 ```json
9898 {
···109109 Note: `is_water` is stored as `0/1` in the database and is returned as `0/1` in list/detail endpoints.
110110111111- **Error Responses:**
112112- - **Code:** 400
113113- - **Content:** `{ "error": "Missing required fields" }`
114114- - **Code:** 400
115115- - **Content:** `{ "error": "Content contains inappropriate language or content" }`
116116- - **Code:** 400
117117- - **Content:** `{ "error": "Captcha verification required" }`
118118- - **Code:** 400
119119- - **Content:** `{ "error": "Invalid captcha" }`
120120- - **Code:** 500
121121- - **Content:** `{ "error": "Failed to upload level" }`
112112+ - **Code:** 400
113113+ - **Content:** `{ "error": "Missing required fields" }`
114114+ - **Code:** 400
115115+ - **Content:** `{ "error": "Content contains inappropriate language or content" }`
116116+ - **Code:** 400
117117+ - **Content:** `{ "error": "Captcha verification required" }`
118118+ - **Code:** 400
119119+ - **Content:** `{ "error": "Invalid captcha" }`
120120+ - **Code:** 500
121121+ - **Content:** `{ "error": "Failed to upload level" }`
122122123123##### List Levels
124124···126126- **Method:** `GET`
127127- **URL Params:** None
128128- **Query Params:**
129129- - `page`: Page number (default: 1)
130130- - `limit`: Results per page (default: 10)
131131- - `author`: Filter by author name (partial match)
132132- - `is_water`: Filter by water levels ("true"/"false")
133133- - `version`: Filter by level version (currently always `3`; reserved for future versions)
134134- - `sort`: Sorting mode. Default is by play count (`plays`). Use `recent` to sort by creation time (`created_at`) and `favorites` to sort by favorite count.
135135- - `reversed_order`: Reverse the sort order (`true` or `1`). By default, sorting is descending.
136136- - `token`: One-time token. If provided and valid, the response is filtered to the single level associated with that token (and pagination becomes `page=1`, `limit=1`). If the token is invalid, the endpoint returns `401`.
129129+ - `page`: Page number (default: 1)
130130+ - `limit`: Results per page (default: 10)
131131+ - `author`: Filter by author name (partial match)
132132+ - `is_water`: Filter by water levels ("true"/"false")
133133+ - `version`: Filter by level version (currently always `3`; reserved for future versions)
134134+ - `sort`: Sorting mode. Default is by play count (`plays`). Use `recent` to sort by creation time (`created_at`) and `favorites` to sort by favorite count.
135135+ - `reversed_order`: Reverse the sort order (`true` or `1`). By default, sorting is descending.
136136+ - `token`: One-time token. If provided and valid, the response is filtered to the single level associated with that token (and pagination becomes `page=1`, `limit=1`). If the token is invalid, the endpoint returns `401`.
137137- **Success Response:**
138138- - **Code:** 200
139139- - **Content:**
138138+ - **Code:** 200
139139+ - **Content:**
140140141141 ```json
142142 {
···165165 ```
166166167167- **Error Response:**
168168- - **Code:** 401
169169- - **Content:** `{ "error": "Invalid token" }`
170170- - **Code:** 500
171171- - **Content:** `{ "error": "Failed to list levels" }`
168168+ - **Code:** 401
169169+ - **Content:** `{ "error": "Invalid token" }`
170170+ - **Code:** 500
171171+ - **Content:** `{ "error": "Failed to list levels" }`
172172173173##### Get Level Details
174174175175- **URL:** `/api/levels/:id`
176176- **Method:** `GET`
177177- **URL Params:**
178178- - `id`: Level ID
178178+ - `id`: Level ID
179179- **Success Response:**
180180- - **Code:** 200
181181- - **Content:**
180180+ - **Code:** 200
181181+ - **Content:**
182182183183 ```json
184184 {
···197197 ```
198198199199- **Error Responses:**
200200- - **Code:** 400
201201- - **Content:** `{ "error": "Invalid level ID" }`
202202- - **Code:** 404
203203- - **Content:** `{ "error": "Level not found" }`
204204- - **Code:** 500
205205- - **Content:** `{ "error": "Failed to get level" }`
200200+ - **Code:** 400
201201+ - **Content:** `{ "error": "Invalid level ID" }`
202202+ - **Code:** 404
203203+ - **Content:** `{ "error": "Level not found" }`
204204+ - **Code:** 500
205205+ - **Content:** `{ "error": "Failed to get level" }`
206206207207##### Download Level
208208209209- **URL:** `/api/levels/:id/download`
210210- **Method:** `GET`
211211- **URL Params:**
212212- - `id`: Level ID
212212+ - `id`: Level ID
213213- **Success Response:**
214214- - **Code:** 200
215215- - **Content:** Binary file download with `.izl3` extension
214214+ - **Code:** 200
215215+ - **Content:** Binary file download with `.izl3` extension
216216- **Error Responses:**
217217- - **Code:** 400
218218- - **Content:** `{ "error": "Invalid level ID" }`
219219- - **Code:** 404
220220- - **Content:** `{ "error": "Level not found" }` or `{ "error": "Level file not found" }`
221221- - **Code:** 500
222222- - **Content:** `{ "error": "Failed to download level" }`
217217+ - **Code:** 400
218218+ - **Content:** `{ "error": "Invalid level ID" }`
219219+ - **Code:** 404
220220+ - **Content:** `{ "error": "Level not found" }` or `{ "error": "Level file not found" }`
221221+ - **Code:** 500
222222+ - **Content:** `{ "error": "Failed to download level" }`
223223224224#### Favorites
225225···228228- **URL:** `/api/levels/:id/favorite`
229229- **Method:** `POST`
230230- **URL Params:**
231231- - `id`: Level ID
231231+ - `id`: Level ID
232232- **Request Body:** None (this endpoint always toggles favorite on/off)
233233- **Success Response:**
234234- - **Code:** 200
235235- - **Content:** `{ "success": true, "level": { "id": 123, "favorites": 5, ... } }`
234234+ - **Code:** 200
235235+ - **Content:** `{ "success": true, "level": { "id": 123, "favorites": 5, ... } }`
236236- **Error Responses:**
237237- - **Code:** 400
238238- - **Content:** `{ "error": "Invalid level ID" }`
239239- - **Code:** 404
240240- - **Content:** `{ "error": "Level not found" }`
241241- - **Code:** 500
242242- - **Content:** `{ "error": "Failed to favorite level" }`
237237+ - **Code:** 400
238238+ - **Content:** `{ "error": "Invalid level ID" }`
239239+ - **Code:** 404
240240+ - **Content:** `{ "error": "Level not found" }`
241241+ - **Code:** 500
242242+ - **Content:** `{ "error": "Failed to favorite level" }`
243243 Note: Captcha verification is not required for favoriting.
244244245245#### Reporting
···249249- **URL:** `/api/levels/:id/report`
250250- **Method:** `POST`
251251- **URL Params:**
252252- - `id`: Level ID
252252+ - `id`: Level ID
253253- **Request Body:**
254254255255 ```json
···259259 ```
260260261261- **Behavior:**
262262- - If `USE_REPORTING=false`, this endpoint returns 404.
263263- - If `DISCORD_REPORT_WEBHOOK_URL` is configured, the server sends the report to the Discord webhook (and attaches the level file if available).
264264- - If no webhook is configured, the server still accepts the report and returns success.
262262+ - If `USE_REPORTING=false`, this endpoint returns 404.
263263+ - If `DISCORD_REPORT_WEBHOOK_URL` is configured, the server sends the report to the Discord webhook (and attaches the level file if available).
264264+ - If no webhook is configured, the server still accepts the report and returns success.
265265- **Success Response:**
266266- - **Code:** 200
267267- - **Content:** `{ "success": true }`
266266+ - **Code:** 200
267267+ - **Content:** `{ "success": true }`
268268- **Error Responses:**
269269- - **Code:** 400
270270- - **Content:** `{ "error": "Invalid input" }`
271271- - **Code:** 404
272272- - **Content:** `{ "error": "Level not found" }`
273273- - **Code:** 500
274274- - **Content:** `{ "error": "Failed to report level" }`
269269+ - **Code:** 400
270270+ - **Content:** `{ "error": "Invalid input" }`
271271+ - **Code:** 404
272272+ - **Content:** `{ "error": "Level not found" }`
273273+ - **Code:** 500
274274+ - **Content:** `{ "error": "Failed to report level" }`
275275276276#### Configuration
277277···280280- **URL:** `/api/config`
281281- **Method:** `GET`
282282- **Success Response:**
283283- - **Code:** 200
284284- - **Content:**
283283+ - **Code:** 200
284284+ - **Content:**
285285286286 ```json
287287 {
+37-37
TODO.md
···33## High Priority
4455- [x] **Separate Test UI and Admin UI Controls**: There should be a way to disable the test UI without also disabling the admin UI
66- - Add `USE_TEST_UI` environment variable to control access to `/index.html` test interface
77- - Keep `USE_PUBLIC_FOLDER` for admin UI but add conditional routing for test interface
88- - This would allow production deployments to disable testing while keeping admin functionality
66+ - Add `USE_TEST_UI` environment variable to control access to `/index.html` test interface
77+ - Keep `USE_PUBLIC_FOLDER` for admin UI but add conditional routing for test interface
88+ - This would allow production deployments to disable testing while keeping admin functionality
991010- [x] _(Removed in favor of NGINX)_ ~~**Fix SSL/HTTPS Implementation**: The current SSL implementation is incomplete and non-functional~~
1111- - The SSL certificate and key are read but not actually used to create an HTTPS server
1212- - Need to implement proper HTTPS server with Express.js or migrate to native Deno HTTPS
1313- - Add proper SSL error handling and validation
1111+ - The SSL certificate and key are read but not actually used to create an HTTPS server
1212+ - Need to implement proper HTTPS server with Express.js or migrate to native Deno HTTPS
1313+ - Add proper SSL error handling and validation
14141515## Medium Priority
16161717- [x] **Environment Configuration Management**
1818- - Create a `.env.example` file with all available environment variables
1919- - Add environment variable validation on startup
2020- - Document all configuration options in README.md
1818+ - Create a `.env.example` file with all available environment variables
1919+ - Add environment variable validation on startup
2020+ - Document all configuration options in README.md
21212222- [x] **API Security Improvements**
2323- - Implement API key authentication for programmatic access
2424- - Add request size limits for file uploads
2525- - Consider adding CSRF protection for admin endpoints
2626- - _(Handled by Cloudflare)_ ~~Add rate limiting for API endpoints (especially `/api/levels` POST)~~
2323+ - Implement API key authentication for programmatic access
2424+ - Add request size limits for file uploads
2525+ - Consider adding CSRF protection for admin endpoints
2626+ - _(Handled by Cloudflare)_ ~~Add rate limiting for API endpoints (especially `/api/levels` POST)~~
27272828- [ ] **Database Improvements**
2929- - Add database migrations system for schema changes
3030- - Implement database connection pooling
3131- - Add database backup/restore functionality
3232- - Add indexes for better query performance (author, created_at, etc.)
2929+ - Add database migrations system for schema changes
3030+ - Implement database connection pooling
3131+ - Add database backup/restore functionality
3232+ - Add indexes for better query performance (author, created_at, etc.)
33333434- [ ] **Error Handling & Logging**
3535- - Implement structured logging (JSON format)
3636- - Add error tracking/monitoring integration
3737- - Improve error messages for better debugging
3838- - Add request/response logging middleware
3535+ - Implement structured logging (JSON format)
3636+ - Add error tracking/monitoring integration
3737+ - Improve error messages for better debugging
3838+ - Add request/response logging middleware
39394040## Low Priority
41414242- [x] **Code Quality & Maintenance**
4343- - Split main.ts into separate modules (routes, middleware, database, etc.)
4444- - Add TypeScript strict mode configuration
4545- - Implement unit tests for core functionality
4646- - _(Decided against: API should not be public. README.md has instructions for the API.)_ ~~Add API documentation (OpenAPI/Swagger)~~
4343+ - Split main.ts into separate modules (routes, middleware, database, etc.)
4444+ - Add TypeScript strict mode configuration
4545+ - Implement unit tests for core functionality
4646+ - _(Decided against: API should not be public. README.md has instructions for the API.)_ ~~Add API documentation (OpenAPI/Swagger)~~
47474848- [ ] **Feature Enhancements**
4949- - Add level search by tags/categories
5050- - Implement level comments/reviews system
5151- - Add user profiles and level collections
5252- - Add level statistics and analytics dashboard
4949+ - Add level search by tags/categories
5050+ - Implement level comments/reviews system
5151+ - Add user profiles and level collections
5252+ - Add level statistics and analytics dashboard
53535454- [ ] **Performance Optimizations**
5555- - Implement response caching for level listings
5656- - Add CDN support for static files
5757- - Optimize database queries with prepared statements
5858- - Add pagination limits and validation
5555+ - Implement response caching for level listings
5656+ - Add CDN support for static files
5757+ - Optimize database queries with prepared statements
5858+ - Add pagination limits and validation
59596060- [ ] **Deployment & DevOps**
6161- - Add Docker containerization
6262- - Create deployment scripts
6363- - Add health check endpoint (`/api/health`)
6464- - Implement graceful shutdown handling
6161+ - Add Docker containerization
6262+ - Create deployment scripts
6363+ - Add health check endpoint (`/api/health`)
6464+ - Implement graceful shutdown handling