A Deno-powered backend service for Plants vs. Zombies: MODDED. [Read-only GitHub mirror] docs.pvzm.net
express typescript expressjs plant deno jspvz pvzm game online backend plants-vs-zombies zombie javascript plants modded vs plantsvszombies openapi pvz noads
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Format "fix readme" Original commit: https://github.com/ROBlNET13/pvzm-backend/commit/dfcd130c23a491555ec8f1562c4ae6100385146d

Co-authored-by: ClaytonTDM <clay@clay.rip>

+77 -77
+1 -1
.markdownlint.jsonc
··· 1 1 { 2 2 "MD013": false, // line length 3 3 "MD010": false, // hard tabs 4 - "MD033": false // inline HTML 4 + "MD033": false, // inline HTML 5 5 }
+76 -76
README.md
··· 21 21 22 22 - **Access**: Navigate to `/admin.html` or click the "Admin Dashboard" link on the main page 23 23 - **Features**: 24 - - View all levels with pagination 25 - - Search levels by name, author, or ID 26 - - Edit level properties (name, author, sun, water status, difficulty, statistics) 27 - - Delete levels (including related files and database entries) > 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). > The admin dashboard also supports one-time-token links for a single edit/delete action: 28 - - Edit: `/admin.html?token=...&action=edit&level=123` 29 - - Delete: `/admin.html?token=...&action=delete&level=123` 24 + - View all levels with pagination 25 + - Search levels by name, author, or ID 26 + - Edit level properties (name, author, sun, water status, difficulty, statistics) 27 + - Delete levels (including related files and database entries) > 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). > The admin dashboard also supports one-time-token links for a single edit/delete action: 28 + - Edit: `/admin.html?token=...&action=edit&level=123` 29 + - Delete: `/admin.html?token=...&action=delete&level=123` 30 30 After a successful token-based edit/delete, the page attempts to call `window.close()` (some browsers only allow this for windows opened by script). 31 31 32 32 ## Getting Started ··· 82 82 - **URL:** `/api/levels` 83 83 - **Method:** `POST` 84 84 - **Content Types:** 85 - - `application/octet-stream` 85 + - `application/octet-stream` 86 86 - **URL Params:** None 87 87 - **Query Params:** (for octet-stream) 88 - - `author`: Author name 89 - - `turnstileResponse`: Captcha verification token (if enabled) 88 + - `author`: Author name 89 + - `turnstileResponse`: Captcha verification token (if enabled) 90 90 - **Notes:** Only IZL3 is supported (v2 is deprecated). 91 91 - **Request Body:** Raw binary level data (`.izl3`), sent as the request body. 92 92 - **Success Response:** 93 - - **Code:** 201 94 - - **Content:** 93 + - **Code:** 201 94 + - **Content:** 95 95 96 96 ```json 97 97 { ··· 108 108 Note: `is_water` is stored as `0/1` in the database and is returned as `0/1` in list/detail endpoints. 109 109 110 110 - **Error Responses:** 111 - - **Code:** 400 112 - - **Content:** `{ "error": "Missing required fields" }` 113 - - **Code:** 400 114 - - **Content:** `{ "error": "Content contains inappropriate language or content" }` 115 - - **Code:** 400 116 - - **Content:** `{ "error": "Captcha verification required" }` 117 - - **Code:** 400 118 - - **Content:** `{ "error": "Invalid captcha" }` 119 - - **Code:** 500 120 - - **Content:** `{ "error": "Failed to upload level" }` 111 + - **Code:** 400 112 + - **Content:** `{ "error": "Missing required fields" }` 113 + - **Code:** 400 114 + - **Content:** `{ "error": "Content contains inappropriate language or content" }` 115 + - **Code:** 400 116 + - **Content:** `{ "error": "Captcha verification required" }` 117 + - **Code:** 400 118 + - **Content:** `{ "error": "Invalid captcha" }` 119 + - **Code:** 500 120 + - **Content:** `{ "error": "Failed to upload level" }` 121 121 122 122 ##### List Levels 123 123 ··· 125 125 - **Method:** `GET` 126 126 - **URL Params:** None 127 127 - **Query Params:** 128 - - `page`: Page number (default: 1) 129 - - `limit`: Results per page (default: 10) 130 - - `author`: Filter by author name (partial match) 131 - - `is_water`: Filter by water levels ("true"/"false") 132 - - `version`: Filter by level version (currently always `3`; reserved for future versions) 133 - - `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. 134 - - `reversed_order`: Reverse the sort order (`true` or `1`). By default, sorting is descending. 135 - - `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`. 128 + - `page`: Page number (default: 1) 129 + - `limit`: Results per page (default: 10) 130 + - `author`: Filter by author name (partial match) 131 + - `is_water`: Filter by water levels ("true"/"false") 132 + - `version`: Filter by level version (currently always `3`; reserved for future versions) 133 + - `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. 134 + - `reversed_order`: Reverse the sort order (`true` or `1`). By default, sorting is descending. 135 + - `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`. 136 136 - **Success Response:** 137 - - **Code:** 200 138 - - **Content:** 137 + - **Code:** 200 138 + - **Content:** 139 139 140 140 ```json 141 141 { ··· 164 164 ``` 165 165 166 166 - **Error Response:** 167 - - **Code:** 401 168 - - **Content:** `{ "error": "Invalid token" }` 169 - - **Code:** 500 170 - - **Content:** `{ "error": "Failed to list levels" }` 167 + - **Code:** 401 168 + - **Content:** `{ "error": "Invalid token" }` 169 + - **Code:** 500 170 + - **Content:** `{ "error": "Failed to list levels" }` 171 171 172 172 ##### Get Level Details 173 173 174 174 - **URL:** `/api/levels/:id` 175 175 - **Method:** `GET` 176 176 - **URL Params:** 177 - - `id`: Level ID 177 + - `id`: Level ID 178 178 - **Success Response:** 179 - - **Code:** 200 180 - - **Content:** 179 + - **Code:** 200 180 + - **Content:** 181 181 182 182 ```json 183 183 { ··· 196 196 ``` 197 197 198 198 - **Error Responses:** 199 - - **Code:** 400 200 - - **Content:** `{ "error": "Invalid level ID" }` 201 - - **Code:** 404 202 - - **Content:** `{ "error": "Level not found" }` 203 - - **Code:** 500 204 - - **Content:** `{ "error": "Failed to get level" }` 199 + - **Code:** 400 200 + - **Content:** `{ "error": "Invalid level ID" }` 201 + - **Code:** 404 202 + - **Content:** `{ "error": "Level not found" }` 203 + - **Code:** 500 204 + - **Content:** `{ "error": "Failed to get level" }` 205 205 206 206 ##### Download Level 207 207 208 208 - **URL:** `/api/levels/:id/download` 209 209 - **Method:** `GET` 210 210 - **URL Params:** 211 - - `id`: Level ID 211 + - `id`: Level ID 212 212 - **Success Response:** 213 - - **Code:** 200 214 - - **Content:** Binary file download with `.izl3` extension 213 + - **Code:** 200 214 + - **Content:** Binary file download with `.izl3` extension 215 215 - **Error Responses:** 216 - - **Code:** 400 217 - - **Content:** `{ "error": "Invalid level ID" }` 218 - - **Code:** 404 219 - - **Content:** `{ "error": "Level not found" }` or `{ "error": "Level file not found" }` 220 - - **Code:** 500 221 - - **Content:** `{ "error": "Failed to download level" }` 216 + - **Code:** 400 217 + - **Content:** `{ "error": "Invalid level ID" }` 218 + - **Code:** 404 219 + - **Content:** `{ "error": "Level not found" }` or `{ "error": "Level file not found" }` 220 + - **Code:** 500 221 + - **Content:** `{ "error": "Failed to download level" }` 222 222 223 223 #### Favorites 224 224 ··· 227 227 - **URL:** `/api/levels/:id/favorite` 228 228 - **Method:** `POST` 229 229 - **URL Params:** 230 - - `id`: Level ID 230 + - `id`: Level ID 231 231 - **Request Body:** None (this endpoint always toggles favorite on/off) 232 232 - **Success Response:** 233 - - **Code:** 200 234 - - **Content:** `{ "success": true, "level": { "id": 123, "favorites": 5, ... } }` 233 + - **Code:** 200 234 + - **Content:** `{ "success": true, "level": { "id": 123, "favorites": 5, ... } }` 235 235 - **Error Responses:** 236 - - **Code:** 400 237 - - **Content:** `{ "error": "Invalid level ID" }` 238 - - **Code:** 404 239 - - **Content:** `{ "error": "Level not found" }` 240 - - **Code:** 500 241 - - **Content:** `{ "error": "Failed to favorite level" }` 236 + - **Code:** 400 237 + - **Content:** `{ "error": "Invalid level ID" }` 238 + - **Code:** 404 239 + - **Content:** `{ "error": "Level not found" }` 240 + - **Code:** 500 241 + - **Content:** `{ "error": "Failed to favorite level" }` 242 242 Note: Captcha verification is not required for favoriting. 243 243 244 244 #### Reporting ··· 248 248 - **URL:** `/api/levels/:id/report` 249 249 - **Method:** `POST` 250 250 - **URL Params:** 251 - - `id`: Level ID 251 + - `id`: Level ID 252 252 - **Request Body:** 253 253 254 254 ```json ··· 258 258 ``` 259 259 260 260 - **Behavior:** 261 - - If `USE_REPORTING=false`, this endpoint returns 404. 262 - - If `DISCORD_REPORT_WEBHOOK_URL` is configured, the server sends the report to the Discord webhook (and attaches the level file if available). 263 - - If no webhook is configured, the server still accepts the report and returns success. 261 + - If `USE_REPORTING=false`, this endpoint returns 404. 262 + - If `DISCORD_REPORT_WEBHOOK_URL` is configured, the server sends the report to the Discord webhook (and attaches the level file if available). 263 + - If no webhook is configured, the server still accepts the report and returns success. 264 264 - **Success Response:** 265 - - **Code:** 200 266 - - **Content:** `{ "success": true }` 265 + - **Code:** 200 266 + - **Content:** `{ "success": true }` 267 267 - **Error Responses:** 268 - - **Code:** 400 269 - - **Content:** `{ "error": "Invalid input" }` 270 - - **Code:** 404 271 - - **Content:** `{ "error": "Level not found" }` 272 - - **Code:** 500 273 - - **Content:** `{ "error": "Failed to report level" }` 268 + - **Code:** 400 269 + - **Content:** `{ "error": "Invalid input" }` 270 + - **Code:** 404 271 + - **Content:** `{ "error": "Level not found" }` 272 + - **Code:** 500 273 + - **Content:** `{ "error": "Failed to report level" }` 274 274 275 275 #### Configuration 276 276 ··· 279 279 - **URL:** `/api/config` 280 280 - **Method:** `GET` 281 281 - **Success Response:** 282 - - **Code:** 200 283 - - **Content:** 282 + - **Code:** 200 283 + - **Content:** 284 284 285 285 ```json 286 286 {