├── .env.example ├── .gitignore ├── .nvmrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── page ├── docs │ ├── index.html │ ├── script.js │ └── styles.css ├── index.html ├── status │ ├── 1xx │ │ ├── 100.html │ │ ├── 101.html │ │ ├── 102.html │ │ └── 103.html │ ├── 2xx │ │ ├── 200.html │ │ ├── 201.html │ │ ├── 202.html │ │ ├── 203.html │ │ ├── 204.html │ │ ├── 205.html │ │ ├── 206.html │ │ ├── 207.html │ │ ├── 208.html │ │ └── 226.html │ ├── 3xx │ │ ├── 300.html │ │ ├── 301.html │ │ ├── 302.html │ │ ├── 303.html │ │ ├── 304.html │ │ ├── 305.html │ │ ├── 307.html │ │ └── 308.html │ ├── 4xx │ │ ├── 400.html │ │ ├── 401.html │ │ ├── 402.html │ │ ├── 403.html │ │ ├── 404.html │ │ ├── 405.html │ │ ├── 406.html │ │ ├── 407.html │ │ ├── 408.html │ │ ├── 409.html │ │ ├── 410.html │ │ ├── 411.html │ │ ├── 412.html │ │ ├── 413.html │ │ ├── 414.html │ │ ├── 415.html │ │ ├── 416.html │ │ ├── 417.html │ │ ├── 418.html │ │ ├── 421.html │ │ ├── 422.html │ │ ├── 423.html │ │ ├── 424.html │ │ ├── 425.html │ │ ├── 426.html │ │ ├── 428.html │ │ ├── 429.html │ │ ├── 431.html │ │ └── 451.html │ ├── 5xx │ │ ├── 500.html │ │ ├── 501.html │ │ ├── 502.html │ │ ├── 503.html │ │ ├── 504.html │ │ ├── 505.html │ │ ├── 506.html │ │ ├── 507.html │ │ ├── 508.html │ │ ├── 510.html │ │ └── 511.html │ └── maintenance │ │ └── maintenance.html └── support.html ├── src ├── api │ ├── ai │ │ └── ai-luminai.js │ ├── anime │ │ ├── anichin-detail.js │ │ ├── anichin-episode.js │ │ ├── anichin-latest.js │ │ ├── anichin-popular.js │ │ └── anichin-search.js │ ├── downloader │ │ ├── facebook.js │ │ ├── instagram.js │ │ ├── spotify.js │ │ ├── tiktok.js │ │ └── youtube.js │ ├── maker │ │ └── maker-brat.js │ └── random │ │ └── random-bluearchive.js ├── discord.js ├── images │ ├── banner.jpg │ ├── icon.png │ ├── icon.webp │ └── preview.png ├── middleware │ └── apikey.js ├── notifications.json ├── settings.json └── sponsor.json └── vercel.json /.env.example: -------------------------------------------------------------------------------- 1 | # Discord Bot Configuration 2 | # Get your bot token from https://discord.com/developers/applications 3 | DISCORD_TOKEN= 4 | 5 | # Owner Configuration 6 | # Your Discord User ID (right-click your profile in Discord and select "Copy User ID") 7 | OWNER_ID= 8 | 9 | # Server Configuration 10 | PORT=3000 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Package lock files 8 | package-lock.json 9 | yarn.lock 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage/ 19 | *.lcov 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage 25 | .grunt 26 | 27 | # Bower dependency directory 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons 34 | build/Release 35 | 36 | # Dependency directories 37 | jspm_packages/ 38 | 39 | # TypeScript cache 40 | *.tsbuildinfo 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Microbundle cache 49 | .rpt2_cache/ 50 | .rts2_cache_cjs/ 51 | .rts2_cache_es/ 52 | .rts2_cache_umd/ 53 | 54 | # Optional REPL history 55 | .node_repl_history 56 | 57 | # Output of 'npm pack' 58 | *.tgz 59 | 60 | # Yarn Integrity file 61 | .yarn-integrity 62 | 63 | # dotenv environment variables file 64 | .env 65 | .env.test 66 | .env.production 67 | 68 | # parcel-bundler cache 69 | .cache 70 | .parcel-cache 71 | 72 | # Next.js build output 73 | .next 74 | 75 | # Nuxt.js build / generate output 76 | .nuxt 77 | dist 78 | 79 | # Gatsby files 80 | .cache/ 81 | public 82 | 83 | # Storybook build outputs 84 | .out 85 | .storybook-out 86 | 87 | # Temporary folders 88 | tmp/ 89 | temp/ 90 | 91 | # Logs 92 | logs 93 | *.log 94 | 95 | # Runtime data 96 | pids 97 | *.pid 98 | *.seed 99 | *.pid.lock 100 | 101 | # Directory for instrumented libs generated by jscoverage/JSCover 102 | lib-cov 103 | 104 | # Coverage directory used by tools like istanbul 105 | coverage 106 | 107 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 108 | .grunt 109 | 110 | # Compiled binary addons (http://nodejs.org/api/addons.html) 111 | build/Release 112 | 113 | # Dependency directory 114 | node_modules 115 | 116 | # Optional npm cache directory 117 | .npm 118 | 119 | # Optional REPL history 120 | .node_repl_history 121 | 122 | # OS generated files 123 | .DS_Store 124 | .DS_Store? 125 | ._* 126 | .Spotlight-V100 127 | .Trashes 128 | ehthumbs.db 129 | Thumbs.db 130 | 131 | # IDE files 132 | .vscode/ 133 | .idea/ 134 | *.swp 135 | *.swo 136 | *~ 137 | 138 | # Build outputs 139 | build/ 140 | dist/ -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Terima kasih atas minat Anda untuk berkontribusi pada proyek Raol-UI REST API! Kami menyambut kontribusi dari siapa saja untuk membantu meningkatkan kualitas dan fungsionalitas API ini. 4 | 5 | ## Lisensi 6 | Proyek Raol-UI REST API ini dirilis di bawah lisensi [MIT License](LICENSE). 7 | 8 | ## Sebelum Berkontribusi 9 | Sebelum membuat perubahan, mohon **diskusikan perubahan yang Anda inginkan** melalui issue, email, atau metode lain dengan pemilik repositori. Pastikan Anda memahami dan mengikuti **Code of Conduct** kami. 10 | 11 | ## Pull Request Process 12 | 1. Pastikan semua dependensi install/build telah dihapus sebelum akhir proses build. 13 | 2. **Update `README.md`** dengan detail perubahan pada interface, termasuk variabel lingkungan baru, port yang diekspos, lokasi file penting, dan parameter kontainer. 14 | 3. **Tingkatkan nomor versi** di contoh file dan `README.md` sesuai dengan versi baru yang diwakili Pull Request ini. Kami menggunakan [SemVer](http://semver.org/) untuk versioning. 15 | 4. Anda dapat melakukan merge Pull Request setelah mendapatkan **persetujuan dari dua developer lain**, atau jika Anda tidak memiliki izin, Anda dapat meminta reviewer kedua untuk melakukan merge untuk Anda. 16 | 17 | ## Code of Conduct 18 | ### Our Pledge 19 | Dalam rangka menciptakan lingkungan yang terbuka dan ramah, kami sebagai kontributor dan maintainer berjanji untuk membuat partisipasi dalam proyek dan komunitas kami menjadi pengalaman bebas pelecehan bagi semua orang, tanpa memandang usia, ukuran tubuh, disabilitas, etnisitas, identitas dan ekspresi gender, tingkat pengalaman, kewarganegaraan, penampilan pribadi, ras, agama, atau identitas dan orientasi seksual. 20 | 21 | ### Our Standards 22 | Contoh perilaku yang berkontribusi pada menciptakan lingkungan positif termasuk: 23 | * Menggunakan bahasa yang ramah dan inklusif 24 | * Menghormati perbedaan pandangan dan pengalaman 25 | * Menerima kritik konstruktif dengan baik 26 | * Fokus pada apa yang terbaik untuk komunitas 27 | * Menunjukkan empati terhadap anggota komunitas lain 28 | 29 | Contoh perilaku tidak dapat diterima oleh peserta termasuk: 30 | * Penggunaan bahasa atau gambar seksual dan perhatian seksual yang tidak diinginkan 31 | * Trolling, komentar menghina/derogatif, dan serangan pribadi atau politik 32 | * Pelecehan publik atau privat 33 | * Menerbitkan informasi pribadi orang lain tanpa izin eksplisit 34 | * Perilaku lain yang dapat dianggap tidak pantas dalam lingkungan profesional 35 | 36 | ### Our Responsibilities 37 | Maintainer proyek bertanggung jawab untuk menjelaskan standar perilaku yang dapat diterima dan diharapkan untuk mengambil tindakan korektif yang tepat dan adil sebagai respons terhadap perilaku tidak dapat diterima. 38 | Maintainer proyek memiliki hak dan tanggung jawab untuk menghapus, mengedit, atau menolak komentar, commit, kode, suntingan wiki, issue, dan kontribusi lain yang tidak sesuai dengan Code of Conduct ini. 39 | 40 | ### Scope 41 | Code of Conduct ini berlaku baik di dalam ruang proyek maupun di ruang publik ketika seseorang mewakili proyek atau komunitasnya. 42 | 43 | ### Enforcement 44 | Kasus perilaku kasar, pelecehan, atau tidak dapat diterima lainnya dapat dilaporkan dengan menghubungi tim proyek di [rolbyte@gmail.com](mailto:raolbyte@gmail.com). Semua keluhan akan ditinjau dan diselidiki dan akan menghasilkan respons yang dianggap perlu dan sesuai dengan keadaan. 45 | 46 | ### Attribution 47 | Code of Conduct ini diadaptasi dari [Contributor Covenant](http://contributor-covenant.org), versi 1.4. 48 | 49 | ## Cara Berkontribusi 50 | - **Laporkan bug**: Gunakan issue tracker untuk melaporkan bug. 51 | - **Ajukan fitur**: Diskusikan ide fitur baru melalui issue. 52 | - **Kirimkan Pull Request**: Ikuti proses Pull Request di atas. 53 | 54 | Terima kasih atas kontribusi Anda! 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 raolbyte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raol APIs 2 | 3 |
4 | Raol APIs Logo 5 | 6 | **Simple and easy to use API with Discord Bot Integration.** 7 | 8 | [![Version](https://img.shields.io/badge/version-LATEST%207.1.0-blue.svg)](https://github.com/raolbyte/Raol-UI) 9 | [![Node.js](https://img.shields.io/badge/node.js-%3E%3D%2020.0.0-green.svg)](https://nodejs.org/) 10 | [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE) 11 | [![Status](https://img.shields.io/badge/status-online-brightgreen.svg)](https://raol-apis.vercel.app) 12 | [![Discord](https://img.shields.io/badge/discord-bot%20ready-7289da.svg)](https://discord.com/developers/applications) 13 |
14 | 15 | ## Table of Contents 16 | 17 | - [Installation](#installation) 18 | - [Quick Start](#quick-start) 19 | - [Discord Bot Integration](#discord-bot-integration) 20 | - [Configuration](#configuration) 21 | - [API Key Management](#api-key-management) 22 | - [Rate Limiting](#rate-limiting) 23 | - [Maintenance Mode](#maintenance-mode) 24 | - [License](#license) 25 | 26 | ## Installation 27 | 28 | ### Prerequisites 29 | 30 | - Node.js >= 20.0.0 31 | - npm or yarn package manager 32 | 33 | ### Node.js Installation (Ubuntu/Debian VPS) 34 | 35 | If you're running this on a VPS with Ubuntu/Debian, we recommend using NVM (Node Version Manager) for easy Node.js management: 36 | 37 | #### Install NVM 38 | 39 | ```bash 40 | # Download and install nvm: 41 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash 42 | 43 | # Reload your shell configuration: 44 | source ~/.bashrc 45 | 46 | # Or manually source nvm: 47 | . "$HOME/.nvm/nvm.sh" 48 | ``` 49 | 50 | #### Install Node.js using NVM 51 | 52 | ```bash 53 | # Install the latest LTS version of Node.js: 54 | nvm install 22 55 | 56 | # Use the installed version: 57 | nvm use 22 58 | 59 | # Set as default: 60 | nvm alias default 22 61 | 62 | # Verify the Node.js version: 63 | node -v # Should print "v22.x.x" 64 | 65 | # Verify npm version: 66 | npm -v # Should print "10.x.x" 67 | ``` 68 | 69 | #### Alternative: Direct Node.js Installation 70 | 71 | If you prefer not to use NVM, you can install Node.js directly: 72 | 73 | ```bash 74 | # Update package index 75 | sudo apt update 76 | 77 | # Install Node.js 20.x 78 | curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - 79 | sudo apt-get install -y nodejs 80 | 81 | # Verify installation 82 | node -v 83 | npm -v 84 | ``` 85 | 86 | ### Clone the Repository 87 | 88 | ```bash 89 | git clone https://github.com/raolbyte/Raol-UI.git 90 | cd Raol-UI 91 | ``` 92 | 93 | ### Install Dependencies 94 | 95 | ```bash 96 | npm install 97 | ``` 98 | 99 | ### Environment Setup 100 | 101 | Copy the example environment file and configure it: 102 | 103 | ```bash 104 | cp .env.example .env 105 | ``` 106 | 107 | Edit the `.env` file with your configuration: 108 | 109 | ```env 110 | # Discord Bot Configuration 111 | # Get your bot token from https://discord.com/developers/applications 112 | DISCORD_TOKEN= 113 | 114 | # Server Configuration 115 | PORT=3000 116 | ``` 117 | 118 | **Note:** The Discord bot is optional. If you don't provide a `DISCORD_TOKEN`, the API will work normally without Discord integration. 119 | 120 | ## Quick Start 121 | 122 | ### Development Mode 123 | 124 | ```bash 125 | npm start 126 | ``` 127 | 128 | ### Production Mode 129 | 130 | ```bash 131 | npm run build 132 | npm run production 133 | ``` 134 | 135 | The server will start on port 3000 (or the port specified in your environment variables). 136 | 137 | ## Discord Bot Integration 138 | 139 | ### Features 140 | 141 | The Discord bot provides powerful API management through slash commands: 142 | 143 | - **`/stats`** - View real-time API statistics with time period support 144 | - **`/maintenance`** - Toggle maintenance mode on/off 145 | - **`/apikey`** - Manage API keys (add, delete, toggle, list) 146 | - **`/endpoint`** - Manage API endpoints (add, delete, list, scan) 147 | 148 | ### Setting Up Discord Bot 149 | 150 | 1. **Create a Discord Application:** 151 | - Go to [Discord Developer Portal](https://discord.com/developers/applications) 152 | - Create a new application 153 | - Go to "Bot" section and create a bot 154 | - Copy the bot token 155 | 156 | 2. **Configure Bot Permissions:** 157 | - Enable "Message Content Intent" in Bot settings 158 | - Invite bot to your server with appropriate permissions 159 | 160 | 3. **Add Token to Environment:** 161 | ```env 162 | DISCORD_TOKEN=your_actual_bot_token_here 163 | ``` 164 | 165 | ### Discord Commands 166 | 167 | #### `/help` 168 | Show available commands and help information with detailed descriptions. 169 | 170 | #### `/stats [action] [time]` 171 | View API statistics with advanced options: 172 | - **Actions:** 173 | - `start_auto` - Start auto-updating stats (updates every 30 seconds) 174 | - `stop_auto` - Stop auto-updating stats 175 | - `view` - View current stats (default) 176 | - **Time Periods:** 177 | - `5m` - Last 5 minutes 178 | - `15m` - Last 15 minutes 179 | - `30m` - Last 30 minutes 180 | - `1h` - Last hour 181 | - `6h` - Last 6 hours 182 | - `12h` - Last 12 hours 183 | - `1d` - Last day 184 | - `3d` - Last 3 days 185 | - `7d` - Last week 186 | 187 | #### `/activity [action] [status]` 188 | Manage bot activity status: 189 | - `set_custom ` - Set custom bot status text 190 | - `reset_auto` - Reset to automatic status rotation 191 | - `show_current` - Show current activity status 192 | 193 | #### `/maintenance [action]` 194 | Toggle maintenance mode: 195 | - `on` - Enable maintenance mode 196 | - `off` - Disable maintenance mode 197 | 198 | #### `/apikey [subcommand]` 199 | Advanced API key management with categories: 200 | - **Add:** `add ` 201 | - Categories: `free`, `premium`, `vip`, `admin` 202 | - Rate limits: `100/minute`, `500/minute`, `1000/minute`, `5000/day`, `10000/day`, `50000/day`, `unlimited` 203 | - **Delete:** `delete ` - Delete API key 204 | - **Toggle:** `toggle ` - Enable/disable API key requirement 205 | - **List:** `list` - List all API keys with details 206 | 207 | #### `/endpoint [subcommand]` 208 | Complete API endpoint management system: 209 | - **Add:** `add [description] [parameters] [optional_parameters]` 210 | - **Name:** Display name for documentation (e.g., "Weather API", "Translate Text") 211 | - **Filename:** File name for endpoint (e.g., "weather", "translate") 212 | - Categories: `ai`, `maker`, `random`, `tools`, `games`, `social`, `news`, `custom` 213 | - Methods: `GET`, `POST`, `PUT`, `DELETE` 214 | - Parameters: Required parameters (comma-separated) 215 | - Optional Parameters: Optional parameters (comma-separated) 216 | - Auto-generates endpoint files and updates documentation 217 | - **Delete:** `delete ` - Delete endpoint and remove from docs 218 | - **List:** `list` - Show all available endpoints 219 | - **Scan:** `scan` - Scan folder structure for existing endpoints 220 | 221 | ### Auto Stats Updates 222 | 223 | The bot automatically updates statistics every 30 seconds when auto-stats is enabled via `/stats start_auto`. The stats are updated in the same channel where the command was used, with message editing to prevent spam. 224 | 225 | ### Endpoint Management 226 | 227 | The Discord bot provides complete endpoint lifecycle management: 228 | 229 | #### Creating Endpoints 230 | ```bash 231 | /endpoint add name:"Weather API" filename:weather category:tools method:GET description:Get weather information parameters:location optional_parameters:format,units 232 | ``` 233 | 234 | This command will: 235 | - **Display Name:** "Weather API" (shown in documentation) 236 | - **Filename:** "weather" (creates `src/api/tools/weather.js`) 237 | - **Path:** `/tools/weather` 238 | - Auto-generate parameter validation 239 | - Update `settings.json` for documentation 240 | - Make the endpoint immediately available at `/docs` 241 | 242 | #### Generated Template Features 243 | - **No Comments**: Clean code without `//` comments 244 | - **Smart Validation**: Required parameters are validated, optional ones are not 245 | - **Auto Examples**: Intelligent parameter examples based on parameter names 246 | - **Complete Documentation**: Full response with examples and usage 247 | - **Error Handling**: Proper error responses and logging 248 | 249 | #### Example Generated Code 250 | ```javascript 251 | import axios from "axios" 252 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 253 | 254 | export default (app) => { 255 | app.get("/tools/weather", createApiKeyMiddleware(), async (req, res) => { 256 | try { 257 | const location = req.query.location 258 | const format = req.query.format 259 | const units = req.query.units 260 | 261 | if (!location) { 262 | return res.status(400).json({ 263 | status: false, 264 | error: "location is required" 265 | }) 266 | } 267 | 268 | res.status(200).json({ 269 | status: true, 270 | message: "Get weather information", 271 | endpoint: "/tools/weather", 272 | method: "GET", 273 | required_parameters: { 274 | "location": "New York" 275 | }, 276 | optional_parameters: { 277 | "format": "example_format", 278 | "units": "example_units" 279 | }, 280 | example: { 281 | url: "https://your-domain.com/tools/weather?location=New York&format=example_format&units=example_units", 282 | method: "GET", 283 | query: { 284 | "location": "New York", 285 | "format": "example_format", 286 | "units": "example_units" 287 | } 288 | } 289 | }) 290 | 291 | } catch (error) { 292 | console.error("tools/weather API Error:", error) 293 | res.status(500).json({ 294 | status: false, 295 | error: error.message || "Internal server error" 296 | }) 297 | } 298 | }) 299 | } 300 | ``` 301 | 302 | ### Bot Activity Management 303 | 304 | The Discord bot features intelligent activity management: 305 | - **Automatic Rotation:** Bot status changes every 30 seconds between different activities 306 | - **Custom Status:** Set custom status text using `/activity set_custom ` 307 | - **Activity Types:** Playing, Watching, Listening, Competing 308 | - **Auto Reset:** Use `/activity reset_auto` to return to automatic rotation 309 | 310 | ## Configuration 311 | 312 | ### Settings.json Structure 313 | 314 | The `src/settings.json` file controls all aspects of your API service: 315 | 316 | ```json 317 | { 318 | "name": "Raol Api'S", 319 | "version": "v7.1.0", 320 | "description": "Experience the next generation of API documentation...", 321 | "maintenance": { 322 | "enabled": false 323 | }, 324 | "bannerImage": "/src/banner.jpg", 325 | "previewImage": "/src/preview.png", 326 | "header": { 327 | "status": "Online" 328 | }, 329 | "apiSettings": { 330 | "creator": "RaolByte", 331 | "requireApikey": false, 332 | "apikey": { 333 | "your-api-key": { 334 | "rateLimit": "5000/day", 335 | "enabled": true 336 | } 337 | } 338 | }, 339 | "categories": [ 340 | { 341 | "name": "Category Name", 342 | "items": [ 343 | { 344 | "name": "API Endpoint Name", 345 | "desc": "Description of the endpoint", 346 | "path": "/endpoint/path", 347 | "status": "ready", 348 | "params": { 349 | "param1": "Parameter description" 350 | } 351 | } 352 | ] 353 | } 354 | ] 355 | } 356 | ``` 357 | 358 | ### Configuration Options 359 | 360 | #### Basic Settings 361 | - `name` - Your API service name 362 | - `version` - Current version number 363 | - `description` - Brief description of your service 364 | - `bannerImage` - Path to banner image 365 | - `previewImage` - Path to preview image 366 | 367 | #### Maintenance Mode 368 | ```json 369 | "maintenance": { 370 | "enabled": true 371 | } 372 | ``` 373 | 374 | #### API Settings 375 | ```json 376 | "apiSettings": { 377 | "creator": "Your Team Name", 378 | "requireApikey": false, 379 | "apikey": { 380 | "api-key-name": { 381 | "rateLimit": "5000/day", 382 | "enabled": true 383 | } 384 | } 385 | } 386 | ``` 387 | 388 | #### Rate Limit Formats 389 | - `"unlimited"` - No rate limiting 390 | - `"100/minute"` - 100 requests per minute 391 | - `"1000/hour"` - 1000 requests per hour 392 | - `"5000/day"` - 5000 requests per day 393 | 394 | ## API Key Management 395 | 396 | ### Enabling API Key Authentication 397 | 398 | Set `requireApikey` to `true` in your `settings.json`: 399 | 400 | ```json 401 | "apiSettings": { 402 | "requireApikey": true, 403 | "apikey": { 404 | "your-secret-key": { 405 | "rateLimit": "1000/day", 406 | "enabled": true 407 | } 408 | } 409 | } 410 | ``` 411 | 412 | ### Using API Keys 413 | 414 | When API keys are required, include them in your requests: 415 | 416 | ```bash 417 | curl "http://localhost:3000/ai/luminai?text=Hello&apikey=your-secret-key" 418 | ``` 419 | 420 | ### API Key Responses 421 | 422 | #### No API Key Provided (when required) 423 | ```json 424 | { 425 | "status": false, 426 | "creator": "RaolByte", 427 | "error": "API key required", 428 | "message": "Please provide a valid API key in the query parameters" 429 | } 430 | ``` 431 | 432 | #### Invalid API Key 433 | ```json 434 | { 435 | "status": false, 436 | "creator": "RaolByte", 437 | "error": "Invalid API key", 438 | "message": "The provided API key is not valid or does not exist" 439 | } 440 | ``` 441 | 442 | ## Rate Limiting 443 | 444 | ### Global Rate Limiting 445 | - **Default:** 50 requests per minute per IP 446 | - **Window:** 1 minute 447 | - **Bypass:** When `requireApikey` is `false`, API endpoints bypass global rate limiting 448 | 449 | ### API Key Rate Limiting 450 | - **Configurable per key** 451 | - **Formats:** `unlimited`, `100/minute`, `1000/hour`, `5000/day` 452 | - **Tracking:** Per API key, not per IP 453 | 454 | ### Rate Limit Responses 455 | 456 | #### Global Rate Limit Exceeded 457 | ```json 458 | { 459 | "status": false, 460 | "creator": "RaolByte", 461 | "error": "Rate limit exceeded", 462 | "message": "You have exceeded the rate limit for this API key" 463 | } 464 | ``` 465 | 466 | ## Maintenance Mode 467 | 468 | Enable maintenance mode to temporarily disable API access: 469 | 470 | ```json 471 | "maintenance": { 472 | "enabled": true 473 | } 474 | ``` 475 | 476 | ### Maintenance Mode Behavior 477 | - **API Endpoints:** Return 503 status with maintenance message 478 | - **Documentation:** Shows maintenance page 479 | - **Bypass Paths:** `/api/settings`, `/assets/`, `/src/`, `/support` 480 | 481 | ## License 482 | 483 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 484 | 485 | ## Support 486 | 487 | - **Issues:** [GitHub Issues](https://github.com/raolbyte/Raol-UI/issues) 488 | - **Contact:** [Support Page](https://whatsapp.com/channel/0029Vb6n9HIDJ6H6oibRvv1D) 489 | 490 | --- 491 | 492 |
493 |

Made with ❤️ by RaolByte

494 |

495 | ⭐ Star this repo • 496 | 🐛 Report Bug • 497 | 💡 Request Feature 498 |

499 |
500 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raol-apis", 3 | "version": "7.1.0", 4 | "description": "Simple and easy to use API documentation", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "node index.js", 9 | "dev": "node --watch index.js", 10 | "build": "npm install", 11 | "test": "echo \"No tests specified\" && exit 0" 12 | }, 13 | "keywords": [ 14 | "api", 15 | "documentation", 16 | "rest", 17 | "express", 18 | "nodejs" 19 | ], 20 | "author": "Raol Mukarrozi", 21 | "license": "MIT", 22 | "dependencies": { 23 | "axios": "^1.6.0", 24 | "chalk": "^5.3.0", 25 | "cheerio": "^1.0.0-rc.12", 26 | "cors": "^2.8.5", 27 | "discord.js": "^14.22.1", 28 | "dotenv": "^17.2.3", 29 | "express": "^4.18.2", 30 | "form-data": "^4.0.0", 31 | "puppeteer-extra": "^3.3.6", 32 | "puppeteer-extra-plugin-stealth": "^2.11.2", 33 | "tough-cookie": "^4.1.3" 34 | }, 35 | "engines": { 36 | "node": "20.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /page/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Raol Api'S 44 | 45 | 46 | 47 | 48 | 49 | 50 | 68 | 69 | 70 | 71 |
72 |
73 | 77 |

Loading...

78 |
79 | 80 |
81 | 82 | 101 | 102 |
103 | 125 | 126 |
127 |
128 |
129 |
130 |

Raol Api'S

131 |
v1.0
132 |
133 | 134 |

A simple and easily customizable API documentation interface.

135 |
136 | 137 | 145 |
146 | 147 | 148 |
149 |

Available APIs

150 |

Explore our collection of powerful and easy-to-use APIs.

151 | 152 |
153 | 154 |
155 |
156 |
157 | 158 |
159 | 181 |
182 |
183 | 184 | 185 | 203 | 204 | 205 | 259 | 260 |
261 | 271 |
272 | 273 | 274 | 291 | 292 | 295 | 296 | 313 | 314 | 315 | 316 | 317 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /page/status/1xx/100.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 100 - Continue 7 | 8 | 28 | 29 | 30 |
31 |
32 |

100

33 | 34 |

Continue

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/1xx/101.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 101 - Switching Protocols 7 | 8 | 28 | 29 | 30 |
31 |
32 |

101

33 | 34 |

Switching Protocols

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/1xx/102.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 102 - Processing 7 | 8 | 28 | 29 | 30 |
31 |
32 |

102

33 | 34 |

Processing

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/1xx/103.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 103 Early Hints - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 103 13 |
14 |

Early Hints

15 |

The server is sending preliminary response headers before the final response.

16 |
17 |

Category: Informational Response

18 |

RFC: 8297

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/2xx/200.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 - OK 7 | 8 | 28 | 29 | 30 |
31 |
32 |

200

33 | 34 |

OK

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/2xx/201.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 201 - Created 7 | 8 | 28 | 29 | 30 |
31 |
32 |

201

33 | 34 |

Created

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/2xx/202.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 202 - Accepted 7 | 8 | 28 | 29 | 30 |
31 |
32 |

202

33 | 34 |

Accepted

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/2xx/203.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 203 Non-Authoritative Information - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 203 13 |
14 |

Non-Authoritative Information

15 |

The request was successful but the information may be from another source.

16 |
17 |

Category: Successful Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/2xx/204.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 204 - No Content 7 | 8 | 28 | 29 | 30 |
31 |
32 |

204

33 | 34 |

No Content

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/2xx/205.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 205 Reset Content - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 205 13 |
14 |

Reset Content

15 |

The request was successful and the user agent should reset the document view.

16 |
17 |

Category: Successful Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/2xx/206.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 206 - Partial Content 7 | 8 | 28 | 29 | 30 |
31 |
32 |

206

33 | 34 |

Partial Content

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/2xx/207.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 207 Multi-Status - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 207 13 |
14 |

Multi-Status

15 |

Multiple status codes might be appropriate for different parts of the response.

16 |
17 |

Category: Successful Response

18 |

RFC: 4918

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/2xx/208.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 208 Already Reported - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 208 13 |
14 |

Already Reported

15 |

The members of a DAV binding have already been enumerated in a previous reply.

16 |
17 |

Category: Successful Response

18 |

RFC: 5842

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/2xx/226.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 226 IM Used - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 226 13 |
14 |

IM Used

15 |

The server has fulfilled a request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.

16 |
17 |

Category: Successful Response

18 |

RFC: 3229

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/3xx/300.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 300 - Multiple Choices 7 | 8 | 28 | 29 | 30 |
31 |
32 |

300

33 | 34 |

Multiple Choices

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/3xx/301.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 301 - Moved Permanently 7 | 8 | 28 | 29 | 30 |
31 |
32 |

301

33 | 34 |

Moved Permanently

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/3xx/302.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 302 - Found 7 | 8 | 28 | 29 | 30 |
31 |
32 |

302

33 | 34 |

Found

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/3xx/303.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 303 See Other - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 303 13 |
14 |

See Other

15 |

The response to the request can be found under another URI using a GET method.

16 |
17 |

Category: Redirection Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/3xx/304.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 304 - Not Modified 7 | 8 | 28 | 29 | 30 |
31 |
32 |

304

33 | 34 |

Not Modified

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/3xx/305.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 305 Use Proxy - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 305 13 |
14 |

Use Proxy

15 |

The requested resource must be accessed through the proxy given by the Location field.

16 |
17 |

Category: Redirection Response

18 |

RFC: 7231 (Deprecated)

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/3xx/307.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 307 - Temporary Redirect 7 | 8 | 28 | 29 | 30 |
31 |
32 |

307

33 | 34 |

Temporary Redirect

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/3xx/308.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 308 Permanent Redirect - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 308 13 |
14 |

Permanent Redirect

15 |

The resource has been permanently moved to another URI, and the request method should not be changed.

16 |
17 |

Category: Redirection Response

18 |

RFC: 7538

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/400.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 400 - Bad Request 7 | 8 | 28 | 29 | 30 |
31 |
32 |

400

33 | 34 |

Bad Request

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/401.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 401 - Unauthorized 7 | 8 | 28 | 29 | 30 |
31 |
32 |

401

33 | 34 |

Unauthorized

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/402.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 402 Payment Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 402 13 |
14 |

Payment Required

15 |

Payment is required to access this resource. This status code is reserved for future use.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 403 - Forbidden 7 | 8 | 28 | 29 | 30 |
31 |
32 |

403

33 | 34 |

Forbidden

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 - Not Found 7 | 8 | 28 | 29 | 30 |
31 |
32 |

404

33 | 34 |

Not Found

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/405.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 405 - Method Not Allowed 7 | 8 | 28 | 29 | 30 |
31 |
32 |

405

33 | 34 |

Method Not Allowed

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/406.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 406 Not Acceptable - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 406 13 |
14 |

Not Acceptable

15 |

The requested resource cannot generate content acceptable according to the Accept headers sent in the request.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/407.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 407 Proxy Authentication Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 407 13 |
14 |

Proxy Authentication Required

15 |

The client must first authenticate itself with the proxy server.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7235

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/408.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 408 - Request Timeout 7 | 8 | 28 | 29 | 30 |
31 |
32 |

408

33 | 34 |

Request Timeout

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/409.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 409 - Conflict 7 | 8 | 28 | 29 | 30 |
31 |
32 |

409

33 | 34 |

Conflict

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/410.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 410 - Gone 7 | 8 | 28 | 29 | 30 |
31 |
32 |

410

33 | 34 |

Gone

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/411.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 411 Length Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 411 13 |
14 |

Length Required

15 |

The server refuses to accept the request without a defined Content-Length header.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/412.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 412 Precondition Failed - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 412 13 |
14 |

Precondition Failed

15 |

One or more conditions given in the request header fields evaluated to false when tested on the server.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7232

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/413.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 413 Payload Too Large - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 413 13 |
14 |

Payload Too Large

15 |

The request entity is larger than limits defined by server.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/414.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 414 URI Too Long - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 414 13 |
14 |

URI Too Long

15 |

The URI requested by the client is longer than the server is willing to interpret.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/415.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 415 Unsupported Media Type - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 415 13 |
14 |

Unsupported Media Type

15 |

The media format of the requested data is not supported by the server.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/416.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 416 Range Not Satisfiable - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 416 13 |
14 |

Range Not Satisfiable

15 |

The range specified by the Range header field in the request cannot be fulfilled.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7233

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/417.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 417 Expectation Failed - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 417 13 |
14 |

Expectation Failed

15 |

The expectation indicated by the Expect request header field cannot be met by this server.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/418.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 418 I'm a teapot - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 🫖 13 |
14 |

I'm a teapot

15 |

The server refuses the attempt to brew coffee with a teapot. This is an April Fools' joke from 1998.

16 |
17 |

Category: Client Error Response

18 |

RFC: 2324 (April Fools)

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/421.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 421 Misdirected Request - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 421 13 |
14 |

Misdirected Request

15 |

The request was directed at a server that is not able to produce a response.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7540

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 422 - Unprocessable Entity 7 | 8 | 28 | 29 | 30 |
31 |
32 |

422

33 | 34 |

Unprocessable Entity

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/423.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 423 Locked - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 423 13 |
14 |

Locked

15 |

The resource that is being accessed is locked.

16 |
17 |

Category: Client Error Response

18 |

RFC: 4918

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/424.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 424 Failed Dependency - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 424 13 |
14 |

Failed Dependency

15 |

The request failed because it depended on another request and that request failed.

16 |
17 |

Category: Client Error Response

18 |

RFC: 4918

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/425.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 425 Too Early - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 425 13 |
14 |

Too Early

15 |

The server is unwilling to risk processing a request that might be replayed.

16 |
17 |

Category: Client Error Response

18 |

RFC: 8470

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/426.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 426 Upgrade Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 426 13 |
14 |

Upgrade Required

15 |

The server refuses to perform the request using the current protocol but might be willing to do so after the client upgrades to a different protocol.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/428.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 428 Precondition Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 428 13 |
14 |

Precondition Required

15 |

The origin server requires the request to be conditional.

16 |
17 |

Category: Client Error Response

18 |

RFC: 6585

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/429.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 429 - Too Many Requests 7 | 8 | 28 | 29 | 30 |
31 |
32 |

429

33 | 34 |

Too Many Requests

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/4xx/431.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 431 Request Header Fields Too Large - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 431 13 |
14 |

Request Header Fields Too Large

15 |

The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.

16 |
17 |

Category: Client Error Response

18 |

RFC: 6585

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/4xx/451.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 451 Unavailable For Legal Reasons - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 451 13 |
14 |

Unavailable For Legal Reasons

15 |

The user-agent requested a resource that cannot legally be provided, such as a web page censored by a government.

16 |
17 |

Category: Client Error Response

18 |

RFC: 7725

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 500 - Internal Server Error 7 | 8 | 28 | 29 | 30 |
31 |
32 |

500

33 | 34 |

Internal Server Error

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/5xx/501.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 501 - Not Implemented 7 | 8 | 28 | 29 | 30 |
31 |
32 |

501

33 | 34 |

Not Implemented

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/5xx/502.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 502 - Bad Gateway 7 | 8 | 28 | 29 | 30 |
31 |
32 |

502

33 | 34 |

Bad Gateway

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/5xx/503.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 503 - Service Unavailable 7 | 8 | 28 | 29 | 30 |
31 |
32 |

503

33 | 34 |

Service Unavailable

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/5xx/504.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 504 - Gateway Timeout 7 | 8 | 28 | 29 | 30 |
31 |
32 |

504

33 | 34 |

Gateway Timeout

35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /page/status/5xx/505.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 505 HTTP Version Not Supported - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 505 13 |
14 |

HTTP Version Not Supported

15 |

The HTTP version used in the request is not supported by the server.

16 |
17 |

Category: Server Error Response

18 |

RFC: 7231

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/506.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 506 Variant Also Negotiates - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 506 13 |
14 |

Variant Also Negotiates

15 |

Transparent content negotiation for the request results in a circular reference.

16 |
17 |

Category: Server Error Response

18 |

RFC: 2295

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/507.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 507 Insufficient Storage - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 507 13 |
14 |

Insufficient Storage

15 |

The server is unable to store the representation needed to complete the request.

16 |
17 |

Category: Server Error Response

18 |

RFC: 4918

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/508.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 508 Loop Detected - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 508 13 |
14 |

Loop Detected

15 |

The server detected an infinite loop while processing the request.

16 |
17 |

Category: Server Error Response

18 |

RFC: 5842

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/510.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 510 Not Extended - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 510 13 |
14 |

Not Extended

15 |

Further extensions to the request are required for the server to fulfill it.

16 |
17 |

Category: Server Error Response

18 |

RFC: 2774

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/5xx/511.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 511 Network Authentication Required - HTTP Status 7 | 8 | 9 | 10 |
11 |
12 | 511 13 |
14 |

Network Authentication Required

15 |

The client needs to authenticate to gain network access.

16 |
17 |

Category: Server Error Response

18 |

RFC: 6585

19 |
20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /page/status/maintenance/maintenance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Raol Api'S - Maintenance 7 | 8 | 9 | 10 | 11 | 12 | 288 | 289 | 290 |
291 |
292 |
293 |
294 | Maintenance Mode 295 |
296 | 297 |
298 | 299 |
300 | 301 |

Website Under Maintenance

302 | 303 |

304 | We're currently upgrading our systems to serve you better. 305 |
306 | Please check back in a few hours. 307 |

308 | 309 |
310 |
311 |
312 | 313 | 317 | 318 |
319 | 320 | Last updated: 321 |
322 |
323 |
324 | 325 | 351 | 352 | -------------------------------------------------------------------------------- /src/api/ai/ai-luminai.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 3 | 4 | export default (app) => { 5 | async function fetchContent(content) { 6 | try { 7 | const response = await axios.post("https://luminai.my.id/", { content }) 8 | return response.data 9 | } catch (error) { 10 | console.error("Error fetching content from LuminAI:", error) 11 | throw error 12 | } 13 | } 14 | app.get("/ai/luminai", createApiKeyMiddleware(), async (req, res) => { 15 | try { 16 | const { text } = req.query 17 | if (!text) { 18 | return res.status(400).json({ status: false, error: "Text is required" }) 19 | } 20 | const { result } = await fetchContent(text) 21 | res.status(200).json({ 22 | status: true, 23 | result, 24 | }) 25 | } catch (error) { 26 | res.status(500).json({ status: false, error: error.message }) 27 | } 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /src/api/anime/anichin-detail.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrapeDetail(url) { 7 | try { 8 | const { data } = await axios.get(url, { 9 | timeout: 30000, 10 | headers: { 11 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 12 | }, 13 | }) 14 | const $ = cheerio.load(data) 15 | const title = $(".entry-title").text().trim() 16 | const thumbnail = $(".thumb img").attr("src") 17 | const rating = $(".rating strong").text().replace("Rating ", "").trim() 18 | const followers = $(".bmc").text().replace("Followed ", "").replace(" people", "").trim() 19 | const synopsis = $(".synp .entry-content").text().trim() 20 | const alternativeTitles = $(".alter").text().trim() 21 | const status = $(".info-content .spe span:contains(\"Status\")").text().replace("Status:", "").trim() 22 | const network = $(".info-content .spe span:contains(\"Network\") a").text().trim() 23 | const studio = $(".info-content .spe span:contains(\"Studio\") a").text().trim() 24 | const released = $(".info-content .spe span:contains(\"Released\")").text().replace("Released:", "").trim() 25 | const duration = $(".info-content .spe span:contains(\"Duration\")").text().replace("Duration:", "").trim() 26 | const season = $(".info-content .spe span:contains(\"Season\") a").text().trim() 27 | const country = $(".info-content .spe span:contains(\"Country\") a").text().trim() 28 | const type = $(".info-content .spe span:contains(\"Type\")").text().replace("Type:", "").trim() 29 | const episodes = $(".info-content .spe span:contains(\"Episodes\")").text().replace("Episodes:", "").trim() 30 | const genres = $(".genxed a").map((_, el) => $(el).text().trim()).get() 31 | 32 | return { 33 | title, 34 | thumbnail, 35 | rating, 36 | followers, 37 | synopsis, 38 | alternativeTitles, 39 | status, 40 | network, 41 | studio, 42 | released, 43 | duration, 44 | season, 45 | country, 46 | type, 47 | episodes, 48 | genres, 49 | } 50 | } catch (error) { 51 | console.error("API Error:", error.message) 52 | throw new Error("Failed to get response from API") 53 | } 54 | } 55 | 56 | app.get("/anime/anichin-detail", createApiKeyMiddleware(), async (req, res) => { 57 | try { 58 | const { url } = req.query 59 | 60 | if (!url) { 61 | return res.status(400).json({ 62 | status: false, 63 | error: "URL is required", 64 | }) 65 | } 66 | 67 | if (typeof url !== "string" || url.trim().length === 0) { 68 | return res.status(400).json({ 69 | status: false, 70 | error: "URL must be a non-empty string", 71 | }) 72 | } 73 | 74 | const data = await scrapeDetail(url.trim()) 75 | 76 | res.status(200).json({ 77 | status: true, 78 | data: data, 79 | timestamp: new Date().toISOString(), 80 | }) 81 | } catch (error) { 82 | res.status(500).json({ 83 | status: false, 84 | error: error.message || "Internal Server Error", 85 | }) 86 | } 87 | }) 88 | 89 | app.post("/anime/anichin-detail", createApiKeyMiddleware(), async (req, res) => { 90 | try { 91 | const { url } = req.body 92 | 93 | if (!url) { 94 | return res.status(400).json({ 95 | status: false, 96 | error: "URL is required", 97 | }) 98 | } 99 | 100 | if (typeof url !== "string" || url.trim().length === 0) { 101 | return res.status(400).json({ 102 | status: false, 103 | error: "URL must be a non-empty string", 104 | }) 105 | } 106 | 107 | const data = await scrapeDetail(url.trim()) 108 | 109 | res.status(200).json({ 110 | status: true, 111 | data: data, 112 | timestamp: new Date().toISOString(), 113 | }) 114 | } catch (error) { 115 | res.status(500).json({ 116 | status: false, 117 | error: error.message || "Internal Server Error", 118 | }) 119 | } 120 | }) 121 | } -------------------------------------------------------------------------------- /src/api/anime/anichin-episode.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrapeEpisodeList(url) { 7 | try { 8 | const { data } = await axios.get(url, { 9 | timeout: 30000, 10 | headers: { 11 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 12 | }, 13 | }) 14 | const $ = cheerio.load(data) 15 | const episodes = [] 16 | 17 | $(".eplister ul li").each((_, element) => { 18 | const episodeNumber = $(element).find(".epl-num").text().trim() 19 | const title = $(element).find(".epl-title").text().trim() 20 | const subStatus = $(element).find(".epl-sub .status").text().trim() 21 | const releaseDate = $(element).find(".epl-date").text().trim() 22 | const link = $(element).find("a").attr("href") 23 | episodes.push({ 24 | episodeNumber: episodeNumber, 25 | title: title, 26 | subStatus: subStatus, 27 | releaseDate: releaseDate, 28 | link: link, 29 | }) 30 | }) 31 | return episodes 32 | } catch (error) { 33 | console.error("API Error:", error.message) 34 | throw new Error("Failed to get response from API") 35 | } 36 | } 37 | 38 | app.get("/anime/anichin-episode", createApiKeyMiddleware(), async (req, res) => { 39 | try { 40 | const { url } = req.query 41 | 42 | if (!url) { 43 | return res.status(400).json({ 44 | status: false, 45 | error: "URL is required", 46 | }) 47 | } 48 | 49 | if (typeof url !== "string" || url.trim().length === 0) { 50 | return res.status(400).json({ 51 | status: false, 52 | error: "URL must be a non-empty string", 53 | }) 54 | } 55 | 56 | const data = await scrapeEpisodeList(url.trim()) 57 | 58 | res.status(200).json({ 59 | status: true, 60 | data: data, 61 | timestamp: new Date().toISOString(), 62 | }) 63 | } catch (error) { 64 | res.status(500).json({ 65 | status: false, 66 | error: error.message || "Internal Server Error", 67 | }) 68 | } 69 | }) 70 | 71 | app.post("/anime/anichin-episode", createApiKeyMiddleware(), async (req, res) => { 72 | try { 73 | const { url } = req.body 74 | 75 | if (!url) { 76 | return res.status(400).json({ 77 | status: false, 78 | error: "URL is required", 79 | }) 80 | } 81 | 82 | if (typeof url !== "string" || url.trim().length === 0) { 83 | return res.status(400).json({ 84 | status: false, 85 | error: "URL must be a non-empty string", 86 | }) 87 | } 88 | 89 | const data = await scrapeEpisodeList(url.trim()) 90 | 91 | res.status(200).json({ 92 | status: true, 93 | data: data, 94 | timestamp: new Date().toISOString(), 95 | }) 96 | } catch (error) { 97 | res.status(500).json({ 98 | status: false, 99 | error: error.message || "Internal Server Error", 100 | }) 101 | } 102 | }) 103 | } -------------------------------------------------------------------------------- /src/api/anime/anichin-latest.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrapeLatestAnime() { 7 | try { 8 | const domain = "https://anichin.team/" 9 | const response = await axios.get(domain, { 10 | timeout: 30000, 11 | headers: { 12 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 13 | }, 14 | }) 15 | const $domain = cheerio.load(response.data) 16 | const redirectScriptContent = $domain("script").filter(function () { 17 | return $domain(this).html()?.includes("setTimeout") 18 | }).html() 19 | 20 | if (!redirectScriptContent) { 21 | throw new Error("Redirect script content not found") 22 | } 23 | 24 | const urlMatch = redirectScriptContent.match(/location\.href = '(https:\/\/[^']+)'/) 25 | if (!urlMatch || !urlMatch[1]) { 26 | throw new Error("Redirect URL not found in script") 27 | } 28 | 29 | const { data } = await axios.get(urlMatch[1], { 30 | timeout: 30000, 31 | headers: { 32 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 33 | }, 34 | }) 35 | const $ = cheerio.load(data) 36 | const results = [] 37 | 38 | $(".listupd.normal .bs").each((_, element) => { 39 | const linkElement = $(element).find("a") 40 | const title = linkElement.attr("title") 41 | const url = linkElement.attr("href") 42 | const episode = $(element).find(".bt .epx").text().trim() 43 | const thumbnail = $(element).find("img").attr("src") 44 | const type = $(element).find(".typez").text().trim() 45 | results.push({ 46 | title: title, 47 | url: url, 48 | episode: episode, 49 | thumbnail: thumbnail, 50 | type: type, 51 | }) 52 | }) 53 | return results 54 | } catch (error) { 55 | console.error("API Error:", error.message) 56 | throw new Error("Error scraping latest anime: " + error.message) 57 | } 58 | } 59 | 60 | app.get("/anime/anichin-latest", createApiKeyMiddleware(), async (req, res) => { 61 | try { 62 | const data = await scrapeLatestAnime() 63 | 64 | res.status(200).json({ 65 | status: true, 66 | data: data, 67 | timestamp: new Date().toISOString(), 68 | }) 69 | } catch (error) { 70 | res.status(500).json({ 71 | status: false, 72 | error: error.message || "Internal Server Error", 73 | }) 74 | } 75 | }) 76 | 77 | app.post("/anime/anichin-latest", createApiKeyMiddleware(), async (req, res) => { 78 | try { 79 | const data = await scrapeLatestAnime() 80 | 81 | res.status(200).json({ 82 | status: true, 83 | data: data, 84 | timestamp: new Date().toISOString(), 85 | }) 86 | } catch (error) { 87 | res.status(500).json({ 88 | status: false, 89 | error: error.message || "Internal Server Error", 90 | }) 91 | } 92 | }) 93 | } -------------------------------------------------------------------------------- /src/api/anime/anichin-popular.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrape(url) { 7 | try { 8 | const { data } = await axios.get(url, { 9 | timeout: 30000, 10 | headers: { 11 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 12 | }, 13 | }) 14 | const $ = cheerio.load(data) 15 | const popularToday = [] 16 | 17 | $(".bixbox .listupd .bsx").each((_, element) => { 18 | const title = $(element).find(".tt").text().trim() 19 | const episode = $(element).find(".bt .epx").text().trim() 20 | const type = $(element).find(".typez").text().trim() 21 | const link = $(element).find("a").attr("href") 22 | const image = $(element).find("img").attr("src") 23 | popularToday.push({ 24 | title: title, 25 | episode: episode, 26 | type: type, 27 | link: link, 28 | image: image, 29 | }) 30 | }) 31 | return popularToday 32 | } catch (error) { 33 | console.error("API Error:", error.message) 34 | throw new Error("Error scraping data: " + error.message) 35 | } 36 | } 37 | 38 | async function getRedirectUrl(domain) { 39 | try { 40 | const response = await axios.get(domain, { 41 | timeout: 30000, 42 | headers: { 43 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 44 | }, 45 | }) 46 | const $ = cheerio.load(response.data) 47 | const redirectScriptContent = $("script").filter(function () { 48 | return $(this).html()?.includes("setTimeout") 49 | }).html() 50 | 51 | if (!redirectScriptContent) { 52 | throw new Error("Redirect script content not found") 53 | } 54 | 55 | const urlMatch = redirectScriptContent.match(/location\.href = '(https:\/\/[^']+)'/) 56 | if (urlMatch && urlMatch[1]) { 57 | return urlMatch[1] 58 | } else { 59 | throw new Error("Redirect URL not found in script") 60 | } 61 | } catch (error) { 62 | console.error("API Error:", error.message) 63 | throw new Error("Error fetching the page: " + error.message) 64 | } 65 | } 66 | 67 | app.get("/anime/anichin-popular", createApiKeyMiddleware(), async (req, res) => { 68 | try { 69 | const domain = "https://anichin.team/" 70 | const redirectUrl = await getRedirectUrl(domain) 71 | const data = await scrape(redirectUrl) 72 | 73 | res.status(200).json({ 74 | status: true, 75 | data: data, 76 | timestamp: new Date().toISOString(), 77 | }) 78 | } catch (error) { 79 | res.status(500).json({ 80 | status: false, 81 | error: error.message || "Internal Server Error", 82 | }) 83 | } 84 | }) 85 | 86 | app.post("/anime/anichin-popular", createApiKeyMiddleware(), async (req, res) => { 87 | try { 88 | const domain = "https://anichin.team/" 89 | const redirectUrl = await getRedirectUrl(domain) 90 | const data = await scrape(redirectUrl) 91 | 92 | res.status(200).json({ 93 | status: true, 94 | data: data, 95 | timestamp: new Date().toISOString(), 96 | }) 97 | } catch (error) { 98 | res.status(500).json({ 99 | status: false, 100 | error: error.message || "Internal Server Error", 101 | }) 102 | } 103 | }) 104 | } -------------------------------------------------------------------------------- /src/api/anime/anichin-search.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrape(query) { 7 | try { 8 | const url = `https://anichin.cafe/?s=${encodeURIComponent(query)}` 9 | const { data } = await axios.get(url, { 10 | timeout: 30000, 11 | headers: { 12 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 13 | }, 14 | }) 15 | const $ = cheerio.load(data) 16 | const results = [] 17 | 18 | $(".listupd article").each((_, el) => { 19 | const title = $(el).find(".tt h2").text().trim() 20 | const type = $(el).find(".typez").text().trim() 21 | const status = $(el).find(".bt .epx").text().trim() 22 | const link = $(el).find("a").attr("href") 23 | const image = $(el).find("img").attr("src") 24 | results.push({ 25 | title: title, 26 | type: type, 27 | status: status, 28 | link: link, 29 | image: image, 30 | }) 31 | }) 32 | return results 33 | } catch (error) { 34 | console.error("API Error:", error.message) 35 | throw new Error("Failed to get response from API") 36 | } 37 | } 38 | 39 | app.get("/anime/anichin-search", createApiKeyMiddleware(), async (req, res) => { 40 | try { 41 | const { query } = req.query 42 | 43 | if (!query) { 44 | return res.status(400).json({ 45 | status: false, 46 | error: "Query is required", 47 | }) 48 | } 49 | 50 | if (typeof query !== "string" || query.trim().length === 0) { 51 | return res.status(400).json({ 52 | status: false, 53 | error: "Query must be a non-empty string", 54 | }) 55 | } 56 | 57 | const results = await scrape(query.trim()) 58 | 59 | if (!results || results.length === 0) { 60 | return res.status(404).json({ 61 | status: false, 62 | error: "No results found for your query", 63 | }) 64 | } 65 | 66 | res.status(200).json({ 67 | status: true, 68 | data: results, 69 | timestamp: new Date().toISOString(), 70 | }) 71 | } catch (error) { 72 | res.status(500).json({ 73 | status: false, 74 | error: error.message || "Internal Server Error", 75 | }) 76 | } 77 | }) 78 | 79 | app.post("/anime/anichin-search", createApiKeyMiddleware(), async (req, res) => { 80 | try { 81 | const { query } = req.body 82 | 83 | if (!query) { 84 | return res.status(400).json({ 85 | status: false, 86 | error: "Query is required", 87 | }) 88 | } 89 | 90 | if (typeof query !== "string" || query.trim().length === 0) { 91 | return res.status(400).json({ 92 | status: false, 93 | error: "Query must be a non-empty string", 94 | }) 95 | } 96 | 97 | const results = await scrape(query.trim()) 98 | 99 | if (!results || results.length === 0) { 100 | return res.status(404).json({ 101 | status: false, 102 | error: "No results found for your query", 103 | }) 104 | } 105 | 106 | res.status(200).json({ 107 | status: true, 108 | data: results, 109 | timestamp: new Date().toISOString(), 110 | }) 111 | } catch (error) { 112 | res.status(500).json({ 113 | status: false, 114 | error: error.message || "Internal Server Error", 115 | }) 116 | } 117 | }) 118 | } -------------------------------------------------------------------------------- /src/api/downloader/facebook.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function fb(url) { 7 | try { 8 | const validUrl = /(?:https?:\/\/(web\.|www\.|m\.)?(facebook|fb)\.(com|watch)\S+)?$/ 9 | if (!validUrl.test(url)) { 10 | throw new Error("Invalid URL provided") 11 | } 12 | 13 | const encodedUrl = encodeURIComponent(url) 14 | const formData = `url=${encodedUrl}&lang=en&type=redirect` 15 | 16 | const response = await axios.post("https://getvidfb.com/", formData, { 17 | headers: { 18 | 'authority': 'getvidfb.com', 19 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 20 | 'accept-language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7', 21 | 'cache-control': 'max-age=0', 22 | 'content-type': 'application/x-www-form-urlencoded', 23 | 'origin': 'https://getvidfb.com', 24 | 'referer': 'https://getvidfb.com/', 25 | 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132"', 26 | 'sec-ch-ua-mobile': '?1', 27 | 'sec-ch-ua-platform': '"Android"', 28 | 'sec-fetch-dest': 'document', 29 | 'sec-fetch-mode': 'navigate', 30 | 'sec-fetch-site': 'same-origin', 31 | 'sec-fetch-user': '?1', 32 | 'upgrade-insecure-requests': '1', 33 | 'user-agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Mobile Safari/537.36' 34 | }, 35 | timeout: 30000, 36 | }) 37 | 38 | const $ = cheerio.load(response.data) 39 | 40 | const videoContainer = $('#snaptik-video') 41 | if (!videoContainer.length) { 42 | throw new Error("Video container not found") 43 | } 44 | 45 | const thumb = videoContainer.find('.snaptik-left img').attr('src') 46 | const title = videoContainer.find('.snaptik-middle h3').text().trim() 47 | 48 | const hasil = [] 49 | 50 | videoContainer.find('.abuttons a').each((_, el) => { 51 | const link = $(el).attr('href') 52 | const spanText = $(el).find('.span-icon span').last().text().trim() 53 | 54 | if (link && spanText && link.startsWith('http')) { 55 | let resolution = 'Unknown' 56 | let format = 'Unknown' 57 | 58 | if (spanText.includes('HD')) { 59 | resolution = 'HD' 60 | format = 'mp4' 61 | } else if (spanText.includes('SD')) { 62 | resolution = 'SD' 63 | format = 'mp4' 64 | } else if (spanText.includes('Mp3') || spanText.includes('Audio')) { 65 | resolution = 'Audio' 66 | format = 'mp3' 67 | } else if (spanText.includes('Photo') || spanText.includes('Jpg')) { 68 | resolution = 'Photo' 69 | format = 'jpg' 70 | } 71 | 72 | hasil.push({ 73 | url: link, 74 | resolution, 75 | format, 76 | }) 77 | } 78 | }) 79 | 80 | if (hasil.length === 0) { 81 | throw new Error("No download links found for the provided URL.") 82 | } 83 | 84 | return { 85 | thumbnail: thumb, 86 | title: title || "Facebook Video", 87 | data: hasil, 88 | } 89 | 90 | } catch (err) { 91 | throw new Error(err.message || "Failed to retrieve data from Facebook video") 92 | } 93 | } 94 | 95 | app.get("/downloader/facebook", createApiKeyMiddleware(), async (req, res) => { 96 | try { 97 | const { url } = req.query 98 | 99 | if (!url) { 100 | return res.status(400).json({ 101 | status: false, 102 | error: "Parameter 'url' is required", 103 | }) 104 | } 105 | 106 | if (typeof url !== "string" || url.trim().length === 0) { 107 | return res.status(400).json({ 108 | status: false, 109 | error: "Parameter 'url' must be a non-empty string", 110 | }) 111 | } 112 | 113 | const result = await fb(url.trim()) 114 | if (!result) { 115 | return res.status(500).json({ 116 | status: false, 117 | error: "No result returned from API", 118 | }) 119 | } 120 | res.status(200).json({ 121 | status: true, 122 | data: result, 123 | timestamp: new Date().toISOString(), 124 | }) 125 | } catch (error) { 126 | res.status(500).json({ 127 | status: false, 128 | error: error.message || "Internal Server Error", 129 | }) 130 | } 131 | }) 132 | 133 | app.post("/downloader/facebook", createApiKeyMiddleware(), async (req, res) => { 134 | try { 135 | const { url } = req.body 136 | 137 | if (!url) { 138 | return res.status(400).json({ 139 | status: false, 140 | error: "Parameter 'url' is required", 141 | }) 142 | } 143 | 144 | if (typeof url !== "string" || url.trim().length === 0) { 145 | return res.status(400).json({ 146 | status: false, 147 | error: "Parameter 'url' must be a non-empty string", 148 | }) 149 | } 150 | 151 | const result = await fb(url.trim()) 152 | if (!result) { 153 | return res.status(500).json({ 154 | status: false, 155 | error: "No result returned from API", 156 | }) 157 | } 158 | res.status(200).json({ 159 | status: true, 160 | data: result, 161 | timestamp: new Date().toISOString(), 162 | }) 163 | } catch (error) { 164 | res.status(500).json({ 165 | status: false, 166 | error: error.message || "Internal Server Error", 167 | }) 168 | } 169 | }) 170 | } -------------------------------------------------------------------------------- /src/api/downloader/instagram.js: -------------------------------------------------------------------------------- 1 | import puppeteer from "puppeteer-extra" 2 | import StealthPlugin from "puppeteer-extra-plugin-stealth" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | puppeteer.use(StealthPlugin()) 6 | 7 | export default (app) => { 8 | function transformResponse(apiResponse) { 9 | if (!apiResponse) { 10 | return [] 11 | } 12 | 13 | let items = [] 14 | 15 | if (Array.isArray(apiResponse)) { 16 | items = apiResponse 17 | } else if (typeof apiResponse === "object") { 18 | items = [apiResponse] 19 | } else { 20 | return [] 21 | } 22 | 23 | return items.map((item) => { 24 | const mainUrl = item.url && Array.isArray(item.url) && item.url[0] ? item.url[0].url : "" 25 | const thumbnailUrl = item.thumb || "" 26 | 27 | return { 28 | thumbnail: thumbnailUrl, 29 | url: mainUrl, 30 | } 31 | }).filter((item) => item.url) 32 | } 33 | 34 | async function tryFastdl(instagramUrl) { 35 | const browser = await puppeteer.launch({ 36 | headless: true, 37 | args: ["--no-sandbox", "--disable-setuid-sandbox"], 38 | }) 39 | 40 | const page = await browser.newPage() 41 | 42 | return new Promise(async (resolve, reject) => { 43 | let apiResponse = null 44 | let responseReceived = false 45 | 46 | page.on("response", async (response) => { 47 | if (response.url().includes("/api/convert") && !responseReceived) { 48 | responseReceived = true 49 | try { 50 | apiResponse = await response.json() 51 | const transformedData = transformResponse(apiResponse) 52 | await browser.close() 53 | resolve(transformedData) 54 | } catch (error) { 55 | await browser.close() 56 | reject(error) 57 | } 58 | } 59 | }) 60 | 61 | await page.setRequestInterception(true) 62 | page.on('request', (req) => { 63 | if (['image', 'stylesheet', 'font'].includes(req.resourceType())) { 64 | req.abort() 65 | } else { 66 | req.continue() 67 | } 68 | }) 69 | 70 | try { 71 | await page.goto("https://fastdl.app/id", { waitUntil: "domcontentloaded" }) 72 | await page.type("#search-form-input", instagramUrl) 73 | await page.click(".search-form__button") 74 | 75 | setTimeout(async () => { 76 | if (!responseReceived) { 77 | await browser.close() 78 | reject(new Error("Timeout waiting for API response")) 79 | } 80 | }, 30000) 81 | 82 | } catch (error) { 83 | await browser.close() 84 | reject(error) 85 | } 86 | }) 87 | } 88 | 89 | async function tryIgram(instagramUrl) { 90 | const browser = await puppeteer.launch({ 91 | headless: true, 92 | args: ["--no-sandbox", "--disable-setuid-sandbox"], 93 | }) 94 | 95 | const page = await browser.newPage() 96 | 97 | return new Promise(async (resolve, reject) => { 98 | let apiResponse = null 99 | let responseReceived = false 100 | 101 | page.on("response", async (response) => { 102 | if (response.url().includes("/api/convert") && !responseReceived) { 103 | responseReceived = true 104 | try { 105 | apiResponse = await response.json() 106 | const transformedData = transformResponse(apiResponse) 107 | await browser.close() 108 | resolve(transformedData) 109 | } catch (error) { 110 | await browser.close() 111 | reject(error) 112 | } 113 | } 114 | }) 115 | 116 | await page.setRequestInterception(true) 117 | page.on('request', (req) => { 118 | if (['image', 'stylesheet', 'font'].includes(req.resourceType())) { 119 | req.abort() 120 | } else { 121 | req.continue() 122 | } 123 | }) 124 | 125 | try { 126 | await page.goto("https://igram.world/id/", { waitUntil: "networkidle2" }) 127 | await page.waitForSelector("#search-form-input", { visible: true }) 128 | await page.type("#search-form-input", instagramUrl) 129 | await page.waitForSelector(".search-form__button", { visible: true }) 130 | await page.evaluate(() => { 131 | document.querySelector(".search-form__button")?.click() 132 | }) 133 | 134 | setTimeout(async () => { 135 | if (!responseReceived) { 136 | await browser.close() 137 | reject(new Error("Timeout waiting for API response")) 138 | } 139 | }, 30000) 140 | 141 | } catch (error) { 142 | await browser.close() 143 | reject(error) 144 | } 145 | }) 146 | } 147 | 148 | async function downloadInstagram(instagramUrl) { 149 | try { 150 | return await tryFastdl(instagramUrl) 151 | } catch (error) { 152 | try { 153 | return await tryIgram(instagramUrl) 154 | } catch (fallbackError) { 155 | throw new Error("Both services failed") 156 | } 157 | } 158 | } 159 | 160 | app.get("/downloader/instagram", createApiKeyMiddleware(), async (req, res) => { 161 | try { 162 | const { url } = req.query 163 | 164 | if (!url) { 165 | return res.status(400).json({ 166 | status: false, 167 | error: "Parameter URL is required", 168 | }) 169 | } 170 | 171 | if (typeof url !== "string" || url.trim().length === 0) { 172 | return res.status(400).json({ 173 | status: false, 174 | error: "URL must be a non-empty string", 175 | }) 176 | } 177 | 178 | const result = await downloadInstagram(url.trim()) 179 | if (!result || result.length === 0) { 180 | return res.status(404).json({ 181 | status: false, 182 | error: "No download links found for the provided URL", 183 | }) 184 | } 185 | res.status(200).json({ 186 | status: true, 187 | data: result, 188 | timestamp: new Date().toISOString(), 189 | }) 190 | } catch (error) { 191 | res.status(500).json({ 192 | status: false, 193 | error: error.message || "Internal Server Error", 194 | }) 195 | } 196 | }) 197 | 198 | app.post("/downloader/instagram", createApiKeyMiddleware(), async (req, res) => { 199 | try { 200 | const { url } = req.body 201 | 202 | if (!url) { 203 | return res.status(400).json({ 204 | status: false, 205 | error: "Parameter URL is required", 206 | }) 207 | } 208 | 209 | if (typeof url !== "string" || url.trim().length === 0) { 210 | return res.status(400).json({ 211 | status: false, 212 | error: "URL must be a non-empty string", 213 | }) 214 | } 215 | 216 | const result = await downloadInstagram(url.trim()) 217 | if (!result || result.length === 0) { 218 | return res.status(404).json({ 219 | status: false, 220 | error: "No download links found for the provided URL", 221 | }) 222 | } 223 | res.status(200).json({ 224 | status: true, 225 | data: result, 226 | timestamp: new Date().toISOString(), 227 | }) 228 | } catch (error) { 229 | res.status(500).json({ 230 | status: false, 231 | error: error.message || "Internal Server Error", 232 | }) 233 | } 234 | }) 235 | } -------------------------------------------------------------------------------- /src/api/downloader/spotify.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 3 | 4 | export default (app) => { 5 | async function scrapeSpotify(url) { 6 | try { 7 | const initialResponse = await axios.get( 8 | `https://api.fabdl.com/spotify/get?url=${encodeURIComponent(url)}`, 9 | { 10 | headers: { 11 | accept: "application/json, text/plain, */*", 12 | "accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", 13 | "sec-ch-ua": "\"Not)A;Brand\";v=\"24\", \"Chromium\";v=\"116\"", 14 | "sec-ch-ua-mobile": "?1", 15 | "sec-ch-ua-platform": "\"Android\"", 16 | "sec-fetch-dest": "empty", 17 | "sec-fetch-mode": "cors", 18 | "sec-fetch-site": "cross-site", 19 | Referer: "https://spotifydownload.org/", 20 | "Referrer-Policy": "strict-origin-when-cross-origin", 21 | }, 22 | }, 23 | ) 24 | 25 | const { result } = initialResponse.data 26 | const trackId = result.type === "album" ? result.tracks[0].id : result.id 27 | 28 | const convertResponse = await axios.get( 29 | `https://api.fabdl.com/spotify/mp3-convert-task/${result.gid}/${trackId}`, 30 | { 31 | headers: { 32 | accept: "application/json, text/plain, */*", 33 | "accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", 34 | "sec-ch-ua": "\"Not)A;Brand\";v=\"24\", \"Chromium\";v=\"116\"", 35 | "sec-ch-ua-mobile": "?1", 36 | "sec-ch-ua-platform": "\"Android\"", 37 | "sec-fetch-dest": "empty", 38 | "sec-fetch-mode": "cors", 39 | "sec-fetch-site": "cross-site", 40 | Referer: "https://spotifydownload.org/", 41 | "Referrer-Policy": "strict-origin-when-cross-origin", 42 | }, 43 | }, 44 | ) 45 | 46 | const tid = convertResponse.data.result.tid 47 | const progressResponse = await axios.get( 48 | `https://api.fabdl.com/spotify/mp3-convert-progress/${tid}`, 49 | { 50 | headers: { 51 | accept: "application/json, text/plain, */*", 52 | "accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", 53 | "sec-ch-ua": "\"Not)A;Brand\";v=\"24\", \"Chromium\";v=\"116\"", 54 | "sec-ch-ua-mobile": "?1", 55 | "sec-ch-ua-platform": "\"Android\"", 56 | "sec-fetch-dest": "empty", 57 | "sec-fetch-mode": "cors", 58 | "sec-fetch-site": "cross-site", 59 | Referer: "https://spotifydownload.org/", 60 | "Referrer-Policy": "strict-origin-when-cross-origin", 61 | }, 62 | }, 63 | ) 64 | 65 | return { 66 | title: result.name, 67 | type: result.type, 68 | artis: result.artists, 69 | durasi: result.type === "album" ? result.tracks[0].duration_ms : result.duration_ms, 70 | image: result.image, 71 | download: `https://api.fabdl.com${progressResponse.data.result.download_url}`, 72 | status: progressResponse.data.result.status, 73 | } 74 | } catch (error) { 75 | console.error("Spotify download error:", error) 76 | throw new Error("Failed to download from Spotify") 77 | } 78 | } 79 | 80 | app.get("/downloader/spotify", createApiKeyMiddleware(), async (req, res) => { 81 | try { 82 | const { url } = req.query 83 | 84 | if (!url) { 85 | return res.status(400).json({ 86 | status: false, 87 | error: "Parameter URL is required", 88 | }) 89 | } 90 | 91 | if (typeof url !== "string" || url.trim().length === 0) { 92 | return res.status(400).json({ 93 | status: false, 94 | error: "Parameter URL must be a non-empty string", 95 | }) 96 | } 97 | 98 | const result = await scrapeSpotify(url.trim()) 99 | if (!result) { 100 | return res.status(500).json({ 101 | status: false, 102 | error: "Failed to download from Spotify or no result returned", 103 | }) 104 | } 105 | res.status(200).json({ 106 | status: true, 107 | data: result, 108 | timestamp: new Date().toISOString(), 109 | }) 110 | } catch (error) { 111 | res.status(500).json({ 112 | status: false, 113 | error: error.message || "Internal Server Error", 114 | }) 115 | } 116 | }) 117 | 118 | app.post("/downloader/spotify", createApiKeyMiddleware(), async (req, res) => { 119 | try { 120 | const { url } = req.body 121 | 122 | if (!url) { 123 | return res.status(400).json({ 124 | status: false, 125 | error: "Parameter URL is required", 126 | }) 127 | } 128 | 129 | if (typeof url !== "string" || url.trim().length === 0) { 130 | return res.status(400).json({ 131 | status: false, 132 | error: "Parameter URL must be a non-empty string", 133 | }) 134 | } 135 | 136 | const result = await scrapeSpotify(url.trim()) 137 | if (!result) { 138 | return res.status(500).json({ 139 | status: false, 140 | error: "Failed to download from Spotify or no result returned", 141 | }) 142 | } 143 | res.status(200).json({ 144 | status: true, 145 | data: result, 146 | timestamp: new Date().toISOString(), 147 | }) 148 | } catch (error) { 149 | res.status(500).json({ 150 | status: false, 151 | error: error.message || "Internal Server Error", 152 | }) 153 | } 154 | }) 155 | } -------------------------------------------------------------------------------- /src/api/downloader/tiktok.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import FormData from "form-data" 4 | import * as tough from "tough-cookie" 5 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 6 | 7 | export default (app) => { 8 | class SnapTikClient { 9 | constructor(config = {}) { 10 | this.config = { 11 | baseURL: "https://snaptik.app", 12 | ...config, 13 | } 14 | 15 | const cookieJar = new tough.CookieJar() 16 | this.axios = axios.create({ 17 | ...this.config, 18 | withCredentials: true, 19 | jar: cookieJar, 20 | headers: { 21 | "User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Mobile Safari/537.36", 22 | "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132"', 23 | "sec-ch-ua-mobile": "?1", 24 | "sec-ch-ua-platform": '"Android"', 25 | "Upgrade-Insecure-Requests": "1", 26 | }, 27 | timeout: 30000, 28 | }) 29 | } 30 | 31 | async get_token() { 32 | const { data } = await this.axios.get("/en2", { 33 | headers: { 34 | "Referer": "https://snaptik.app/en2", 35 | }, 36 | }) 37 | const $ = cheerio.load(data) 38 | return $("input[name=\"token\"]").val() 39 | } 40 | 41 | async get_script(url) { 42 | const form = new FormData() 43 | const token = await this.get_token() 44 | 45 | if (!token) { 46 | throw new Error("Failed to get token") 47 | } 48 | 49 | form.append("url", url) 50 | form.append("lang", "en2") 51 | form.append("token", token) 52 | 53 | const { data } = await this.axios.post("/abc2.php", form, { 54 | headers: { 55 | ...form.getHeaders(), 56 | "authority": "snaptik.app", 57 | "accept": "*/*", 58 | "accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", 59 | "origin": "https://snaptik.app", 60 | "referer": "https://snaptik.app/en2", 61 | "sec-fetch-dest": "empty", 62 | "sec-fetch-mode": "cors", 63 | "sec-fetch-site": "same-origin", 64 | }, 65 | }) 66 | return data 67 | } 68 | 69 | async eval_script(script1) { 70 | const script2 = await new Promise((resolve) => 71 | Function("eval", script1)(resolve) 72 | ) 73 | 74 | return new Promise((resolve, reject) => { 75 | let html = "" 76 | const mockObjects = { 77 | $: () => ({ 78 | remove() {}, 79 | style: { display: "" }, 80 | get innerHTML() { 81 | return html 82 | }, 83 | set innerHTML(t) { 84 | html = t 85 | }, 86 | }), 87 | app: { showAlert: reject }, 88 | document: { getElementById: () => ({ src: "" }) }, 89 | fetch: (a) => { 90 | resolve({ html, oembed_url: a }) 91 | return { json: () => ({ thumbnail_url: "" }) } 92 | }, 93 | gtag: () => 0, 94 | Math: { round: () => 0 }, 95 | XMLHttpRequest: function () { 96 | return { open() {}, send() {} } 97 | }, 98 | window: { location: { hostname: "snaptik.app" } }, 99 | } 100 | 101 | try { 102 | Function( 103 | ...Object.keys(mockObjects), 104 | script2 105 | )(...Object.values(mockObjects)) 106 | } catch (error) { 107 | console.log("Eval error saved to eval.txt:", error.message) 108 | reject(error) 109 | } 110 | }) 111 | } 112 | 113 | async get_hd_video(hdUrl, backupUrl) { 114 | try { 115 | const { data } = await this.axios.get(hdUrl) 116 | if (data && data.url) { 117 | return data.url 118 | } 119 | } catch (error) { 120 | console.log("HD URL failed, using backup:", error.message) 121 | } 122 | return backupUrl 123 | } 124 | 125 | async parse_html(html) { 126 | const $ = cheerio.load(html) 127 | const isVideo = !$("div.render-wrapper").length 128 | 129 | const thumbnail = $(".avatar").attr("src") || $("#thumbnail").attr("src") 130 | const title = $(".video-title").text().trim() 131 | const creator = $(".info span").text().trim() 132 | 133 | if (isVideo) { 134 | const hdButton = $("div.video-links > button[data-tokenhd]") 135 | const hdTokenUrl = hdButton.data("tokenhd") 136 | const backupUrl = hdButton.data("backup") 137 | 138 | let hdUrl = null 139 | if (hdTokenUrl) { 140 | hdUrl = await this.get_hd_video(hdTokenUrl, backupUrl) 141 | } 142 | 143 | const videoUrls = [ 144 | hdUrl || backupUrl, 145 | ...$("div.video-links > a:not(a[href=\"/\"])") 146 | .map((_, elem) => $(elem).attr("href")) 147 | .get() 148 | .filter((url) => url && !url.includes("play.google.com")) 149 | .map((x) => (x.startsWith("/") ? this.config.baseURL + x : x)), 150 | ].filter(Boolean) 151 | 152 | return { 153 | type: "video", 154 | urls: videoUrls, 155 | metadata: { 156 | title: title || null, 157 | description: title || null, 158 | thumbnail: thumbnail || null, 159 | creator: creator || null, 160 | }, 161 | } 162 | } else { 163 | const photos = $("div.columns > div.column > div.photo") 164 | .map((_, elem) => ({ 165 | urls: [ 166 | $(elem).find("img[alt=\"Photo\"]").attr("src"), 167 | $(elem) 168 | .find("a[data-event=\"download_albumPhoto_photo\"]") 169 | .attr("href"), 170 | ], 171 | })) 172 | .get() 173 | 174 | return { 175 | type: photos.length === 1 ? "photo" : "slideshow", 176 | urls: 177 | photos.length === 1 178 | ? photos[0].urls 179 | : photos.map((photo) => photo.urls), 180 | metadata: { 181 | title: title || null, 182 | description: title || null, 183 | thumbnail: thumbnail || null, 184 | creator: creator || null, 185 | }, 186 | } 187 | } 188 | } 189 | 190 | async process(url) { 191 | try { 192 | const script = await this.get_script(url) 193 | const { html, oembed_url } = await this.eval_script(script) 194 | const result = await this.parse_html(html) 195 | 196 | return { 197 | original_url: url, 198 | oembed_url, 199 | type: result.type, 200 | urls: result.urls, 201 | metadata: result.metadata, 202 | } 203 | } catch (error) { 204 | console.error("Process error:", error.message) 205 | return { 206 | original_url: url, 207 | error: error.message, 208 | } 209 | } 210 | } 211 | } 212 | 213 | async function scrapeTiktok(url) { 214 | try { 215 | const client = new SnapTikClient() 216 | return await client.process(url) 217 | } catch (error) { 218 | console.error("Tiktok scrape error:", error) 219 | return null 220 | } 221 | } 222 | 223 | app.get("/downloader/tiktok", createApiKeyMiddleware(), async (req, res) => { 224 | try { 225 | const { url } = req.query 226 | 227 | if (!url) { 228 | return res.status(400).json({ 229 | status: false, 230 | error: "URL parameter is required", 231 | }) 232 | } 233 | 234 | if (typeof url !== "string" || url.trim().length === 0) { 235 | return res.status(400).json({ 236 | status: false, 237 | error: "URL must be a non-empty string", 238 | }) 239 | } 240 | 241 | const result = await scrapeTiktok(url.trim()) 242 | if (!result) { 243 | return res.status(500).json({ 244 | status: false, 245 | error: result?.error || "Failed to process TikTok URL", 246 | }) 247 | } 248 | res.status(200).json({ 249 | status: true, 250 | data: result, 251 | timestamp: new Date().toISOString(), 252 | }) 253 | } catch (error) { 254 | res.status(500).json({ 255 | status: false, 256 | error: error.message || "Internal Server Error", 257 | }) 258 | } 259 | }) 260 | 261 | app.post("/downloader/tiktok", createApiKeyMiddleware(), async (req, res) => { 262 | try { 263 | const { url } = req.body 264 | 265 | if (!url) { 266 | return res.status(400).json({ 267 | status: false, 268 | error: "URL parameter is required", 269 | }) 270 | } 271 | 272 | if (typeof url !== "string" || url.trim().length === 0) { 273 | return res.status(400).json({ 274 | status: false, 275 | error: "URL must be a non-empty string", 276 | }) 277 | } 278 | 279 | const result = await scrapeTiktok(url.trim()) 280 | if (!result) { 281 | return res.status(500).json({ 282 | status: false, 283 | error: result?.error || "Failed to process TikTok URL", 284 | }) 285 | } 286 | res.status(200).json({ 287 | status: true, 288 | data: result, 289 | timestamp: new Date().toISOString(), 290 | }) 291 | } catch (error) { 292 | res.status(500).json({ 293 | status: false, 294 | error: error.message || "Internal Server Error", 295 | }) 296 | } 297 | }) 298 | } -------------------------------------------------------------------------------- /src/api/downloader/youtube.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as cheerio from "cheerio" 3 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 4 | 5 | export default (app) => { 6 | async function scrapeYoutubeCommunity(url) { 7 | try { 8 | const { data: response } = await axios.get(url) 9 | const $ = cheerio.load(response) 10 | const ytInitialData = JSON.parse( 11 | $("script") 12 | .text() 13 | .match(/ytInitialData = ({.*?});/)?.[1] || "{}" 14 | ) 15 | 16 | const posts = 17 | ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents 18 | .flatMap((section) => section.itemSectionRenderer?.contents || []) 19 | .map((item) => { 20 | const postRenderer = 21 | item.backstagePostThreadRenderer?.post?.backstagePostRenderer 22 | if (!postRenderer) return null 23 | 24 | const images = 25 | postRenderer.backstageAttachment?.postMultiImageRenderer 26 | ?.images || [] 27 | const imageUrls = images.map((imageObj) => { 28 | const thumbnails = 29 | imageObj.backstageImageRenderer.image.thumbnails 30 | return thumbnails[thumbnails.length - 1].url 31 | }) 32 | 33 | return { 34 | postId: postRenderer.postId, 35 | author: postRenderer.authorText.simpleText, 36 | content: 37 | postRenderer.contentText?.runs 38 | ?.map((run) => run.text) 39 | .join("") || "", 40 | images: imageUrls, 41 | } 42 | }) 43 | .filter(Boolean) 44 | 45 | return posts[0] || null 46 | } catch (error) { 47 | console.error("Youtube Community scrape error:", error.message) 48 | throw new Error("Failed to get response from API") 49 | } 50 | } 51 | 52 | app.get("/downloader/youtube", createApiKeyMiddleware(), async (req, res) => { 53 | try { 54 | const { url } = req.query 55 | 56 | if (!url) { 57 | return res.status(400).json({ 58 | status: false, 59 | error: "URL parameter is required", 60 | }) 61 | } 62 | 63 | if (typeof url !== "string" || url.trim().length === 0) { 64 | return res.status(400).json({ 65 | status: false, 66 | error: "URL must be a non-empty string", 67 | }) 68 | } 69 | 70 | const result = await scrapeYoutubeCommunity(url.trim()) 71 | if (!result) { 72 | return res.status(404).json({ 73 | status: false, 74 | error: "Failed to fetch community post or no post found", 75 | }) 76 | } 77 | res.status(200).json({ 78 | status: true, 79 | data: result, 80 | timestamp: new Date().toISOString(), 81 | }) 82 | } catch (error) { 83 | res.status(500).json({ 84 | status: false, 85 | error: error.message || "Internal Server Error", 86 | }) 87 | } 88 | }) 89 | 90 | app.post("/downloader/youtube", createApiKeyMiddleware(), async (req, res) => { 91 | try { 92 | const { url } = req.body 93 | 94 | if (!url) { 95 | return res.status(400).json({ 96 | status: false, 97 | error: "URL parameter is required", 98 | }) 99 | } 100 | 101 | if (typeof url !== "string" || url.trim().length === 0) { 102 | return res.status(400).json({ 103 | status: false, 104 | error: "URL must be a non-empty string", 105 | }) 106 | } 107 | 108 | const result = await scrapeYoutubeCommunity(url.trim()) 109 | if (!result) { 110 | return res.status(404).json({ 111 | status: false, 112 | error: "Failed to fetch community post or no post found", 113 | }) 114 | } 115 | res.status(200).json({ 116 | status: true, 117 | data: result, 118 | timestamp: new Date().toISOString(), 119 | }) 120 | } catch (error) { 121 | res.status(500).json({ 122 | status: false, 123 | error: error.message || "Internal Server Error", 124 | }) 125 | } 126 | }) 127 | } -------------------------------------------------------------------------------- /src/api/maker/maker-brat.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 3 | 4 | export default (app) => { 5 | async function generateBratImage(text, background = null, color = null) { 6 | try { 7 | const params = new URLSearchParams() 8 | params.append("text", text) 9 | 10 | if (background) { 11 | params.append("background", background) 12 | } 13 | 14 | if (color) { 15 | params.append("color", color) 16 | } 17 | 18 | const response = await axios.get(`https://raolbyte-brat.hf.space/maker/brat?${params.toString()}`, { 19 | timeout: 30000, 20 | headers: { 21 | "User-Agent": "Raol-APIs/2.0.0", 22 | }, 23 | }) 24 | 25 | if (response.data && response.data.image_url) { 26 | const imageResponse = await axios.get(response.data.image_url, { 27 | responseType: "arraybuffer", 28 | timeout: 30000, 29 | headers: { 30 | "User-Agent": "Raol-APIs/2.0.0", 31 | }, 32 | }) 33 | 34 | return Buffer.from(imageResponse.data) 35 | } else { 36 | throw new Error("Invalid response from BRAT API") 37 | } 38 | } catch (error) { 39 | console.error("Error generating BRAT image:", error) 40 | 41 | if (error.code === "ECONNABORTED") { 42 | throw new Error("Request timeout - BRAT API took too long to respond") 43 | } else if (error.response) { 44 | throw new Error(`BRAT API error: ${error.response.status} - ${error.response.statusText}`) 45 | } else if (error.request) { 46 | throw new Error("Network error - Could not reach BRAT API") 47 | } else { 48 | throw new Error(`BRAT generation failed: ${error.message}`) 49 | } 50 | } 51 | } 52 | 53 | async function generateBratVideo(text, background = null, color = null) { 54 | try { 55 | const params = new URLSearchParams() 56 | params.append("text", text) 57 | 58 | if (background) { 59 | params.append("background", background) 60 | } 61 | 62 | if (color) { 63 | params.append("color", color) 64 | } 65 | 66 | const response = await axios.get(`https://raolbyte-brat.hf.space/maker/bratvid?${params.toString()}`, { 67 | timeout: 60000, 68 | headers: { 69 | "User-Agent": "Raol-APIs/2.0.0", 70 | }, 71 | }) 72 | 73 | if (response.data && response.data.video_url) { 74 | const videoResponse = await axios.get(response.data.video_url, { 75 | responseType: "arraybuffer", 76 | timeout: 60000, 77 | headers: { 78 | "User-Agent": "Raol-APIs/2.0.0", 79 | }, 80 | }) 81 | 82 | return Buffer.from(videoResponse.data) 83 | } else { 84 | throw new Error("Invalid response from BRATVID API") 85 | } 86 | } catch (error) { 87 | console.error("Error generating BRAT video:", error) 88 | 89 | if (error.code === "ECONNABORTED") { 90 | throw new Error("Request timeout - BRATVID API took too long to respond") 91 | } else if (error.response) { 92 | throw new Error(`BRATVID API error: ${error.response.status} - ${error.response.statusText}`) 93 | } else if (error.request) { 94 | throw new Error("Network error - Could not reach BRATVID API") 95 | } else { 96 | throw new Error(`BRATVID generation failed: ${error.message}`) 97 | } 98 | } 99 | } 100 | 101 | app.get("/maker/brat", createApiKeyMiddleware(), async (req, res) => { 102 | try { 103 | const { text, background, color } = req.query 104 | 105 | if (!text) { 106 | return res.status(400).json({ 107 | status: false, 108 | error: "Missing required parameter", 109 | message: "The 'text' parameter is required", 110 | }) 111 | } 112 | 113 | if (text.length > 500) { 114 | return res.status(400).json({ 115 | status: false, 116 | error: "Text too long", 117 | message: "Text must be 500 characters or less", 118 | }) 119 | } 120 | 121 | if (background && !/^#[0-9A-Fa-f]{6}$/.test(background)) { 122 | return res.status(400).json({ 123 | status: false, 124 | error: "Invalid background color", 125 | message: "Background color must be in hex format (e.g., #000000)", 126 | }) 127 | } 128 | 129 | if (color && !/^#[0-9A-Fa-f]{6}$/.test(color)) { 130 | return res.status(400).json({ 131 | status: false, 132 | error: "Invalid text color", 133 | message: "Text color must be in hex format (e.g., #FFFFFF)", 134 | }) 135 | } 136 | 137 | const imageBuffer = await generateBratImage(text, background, color) 138 | 139 | res.setHeader("Content-Type", "image/png") 140 | res.setHeader("Content-Length", imageBuffer.length) 141 | res.setHeader("Cache-Control", "public, max-age=3600") 142 | res.setHeader("Content-Disposition", `inline; filename="brat_${Date.now()}.png"`) 143 | 144 | res.end(imageBuffer) 145 | } catch (error) { 146 | console.error("BRAT API Error:", error) 147 | 148 | res.status(500).json({ 149 | status: false, 150 | error: "Image generation failed", 151 | message: error.message || "Failed to generate BRAT image", 152 | }) 153 | } 154 | }) 155 | 156 | app.get("/maker/bratvid", createApiKeyMiddleware(), async (req, res) => { 157 | try { 158 | const { text, background, color } = req.query 159 | 160 | if (!text) { 161 | return res.status(400).json({ 162 | status: false, 163 | error: "Missing required parameter", 164 | message: "The 'text' parameter is required", 165 | }) 166 | } 167 | 168 | if (text.length > 500) { 169 | return res.status(400).json({ 170 | status: false, 171 | error: "Text too long", 172 | message: "Text must be 500 characters or less", 173 | }) 174 | } 175 | 176 | if (background && !/^#[0-9A-Fa-f]{6}$/.test(background)) { 177 | return res.status(400).json({ 178 | status: false, 179 | error: "Invalid background color", 180 | message: "Background color must be in hex format (e.g., #000000)", 181 | }) 182 | } 183 | 184 | if (color && !/^#[0-9A-Fa-f]{6}$/.test(color)) { 185 | return res.status(400).json({ 186 | status: false, 187 | error: "Invalid text color", 188 | message: "Text color must be in hex format (e.g., #FFFFFF)", 189 | }) 190 | } 191 | 192 | const videoBuffer = await generateBratVideo(text, background, color) 193 | 194 | res.setHeader("Content-Type", "video/mp4") 195 | res.setHeader("Content-Length", videoBuffer.length) 196 | res.setHeader("Cache-Control", "public, max-age=3600") 197 | res.setHeader("Content-Disposition", `inline; filename="bratvid_${Date.now()}.mp4"`) 198 | res.setHeader("Accept-Ranges", "bytes") 199 | res.setHeader("Access-Control-Allow-Origin", "*") 200 | res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS") 201 | res.setHeader("Access-Control-Allow-Headers", "Range") 202 | 203 | if (req.method === "HEAD") { 204 | return res.end() 205 | } 206 | 207 | const range = req.headers.range 208 | if (range) { 209 | const parts = range.replace(/bytes=/, "").split("-") 210 | const start = Number.parseInt(parts[0], 10) 211 | const end = parts[1] ? Number.parseInt(parts[1], 10) : videoBuffer.length - 1 212 | const chunksize = end - start + 1 213 | const chunk = videoBuffer.slice(start, end + 1) 214 | 215 | res.status(206) 216 | res.setHeader("Content-Range", `bytes ${start}-${end}/${videoBuffer.length}`) 217 | res.setHeader("Content-Length", chunksize) 218 | res.end(chunk) 219 | } else { 220 | res.end(videoBuffer) 221 | } 222 | } catch (error) { 223 | console.error("BRATVID API Error:", error) 224 | 225 | res.status(500).json({ 226 | status: false, 227 | error: "Video generation failed", 228 | message: error.message || "Failed to generate BRAT video", 229 | }) 230 | } 231 | }) 232 | } 233 | -------------------------------------------------------------------------------- /src/api/random/random-bluearchive.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { createApiKeyMiddleware } from "../../middleware/apikey.js" 3 | 4 | export default (app) => { 5 | async function bluearchive() { 6 | try { 7 | const { data } = await axios.get( 8 | `https://raw.githubusercontent.com/rynxzyy/blue-archive-r-img/refs/heads/main/links.json`, 9 | ) 10 | const response = await axios.get(data[Math.floor(data.length * Math.random())], { responseType: "arraybuffer" }) 11 | return Buffer.from(response.data) 12 | } catch (error) { 13 | throw error 14 | } 15 | } 16 | 17 | app.get("/random/ba", createApiKeyMiddleware(), async (req, res) => { 18 | try { 19 | const imageBuffer = await bluearchive() 20 | res.writeHead(200, { 21 | "Content-Type": "image/png", 22 | "Content-Length": imageBuffer.length, 23 | }) 24 | res.end(imageBuffer) 25 | } catch (error) { 26 | res.status(500).json({ 27 | status: false, 28 | error: error.message || "Failed to fetch Blue Archive image", 29 | }) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /src/images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raolbyte/Raol-UI/d0154b8253f6d9b9b286308e8bad0aac18d48af1/src/images/banner.jpg -------------------------------------------------------------------------------- /src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raolbyte/Raol-UI/d0154b8253f6d9b9b286308e8bad0aac18d48af1/src/images/icon.png -------------------------------------------------------------------------------- /src/images/icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raolbyte/Raol-UI/d0154b8253f6d9b9b286308e8bad0aac18d48af1/src/images/icon.webp -------------------------------------------------------------------------------- /src/images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raolbyte/Raol-UI/d0154b8253f6d9b9b286308e8bad0aac18d48af1/src/images/preview.png -------------------------------------------------------------------------------- /src/middleware/apikey.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | 4 | const settingsPath = path.join(process.cwd(), 'src', 'settings.json') 5 | 6 | function loadSettings() { 7 | try { 8 | const data = fs.readFileSync(settingsPath, 'utf8') 9 | return JSON.parse(data) 10 | } catch (error) { 11 | return null 12 | } 13 | } 14 | 15 | const rateLimitMap = new Map() 16 | 17 | function parseRateLimit(rateLimitString) { 18 | if (rateLimitString === 'unlimited') { 19 | return { maxRequests: Infinity, windowMs: 0 } 20 | } 21 | 22 | const match = rateLimitString.match(/^(\d+)\/(minute|hour|day)$/) 23 | if (!match) { 24 | return { maxRequests: 50, windowMs: 60 * 1000 } 25 | } 26 | 27 | const [, maxRequests, unit] = match 28 | let windowMs 29 | 30 | switch (unit) { 31 | case 'minute': 32 | windowMs = 60 * 1000 33 | break 34 | case 'hour': 35 | windowMs = 60 * 60 * 1000 36 | break 37 | case 'day': 38 | windowMs = 24 * 60 * 60 * 1000 39 | break 40 | default: 41 | windowMs = 60 * 1000 42 | } 43 | 44 | return { maxRequests: parseInt(maxRequests), windowMs } 45 | } 46 | 47 | function checkRateLimit(apikey) { 48 | const settings = loadSettings() 49 | if (!settings || !settings.apiSettings || !settings.apiSettings.apikey) return false 50 | 51 | const apikeyConfig = settings.apiSettings.apikey[apikey] 52 | if (!apikeyConfig || !apikeyConfig.enabled) return false 53 | 54 | if (apikeyConfig.rateLimit === 'unlimited') return true 55 | 56 | const { maxRequests, windowMs } = parseRateLimit(apikeyConfig.rateLimit) 57 | const now = Date.now() 58 | const key = `${apikey}_${Math.floor(now / windowMs)}` 59 | 60 | if (!rateLimitMap.has(key)) { 61 | rateLimitMap.set(key, { count: 0, resetTime: now + windowMs }) 62 | } 63 | 64 | const limitData = rateLimitMap.get(key) 65 | 66 | if (now > limitData.resetTime) { 67 | limitData.count = 0 68 | limitData.resetTime = now + windowMs 69 | } 70 | 71 | if (limitData.count >= maxRequests) { 72 | return false 73 | } 74 | 75 | limitData.count++ 76 | return true 77 | } 78 | 79 | export function validateApiKey(req, res, next) { 80 | const { apikey } = req.query 81 | 82 | if (!apikey) { 83 | const settings = loadSettings() 84 | return res.status(401).json({ 85 | status: false, 86 | creator: settings?.apiSettings?.creator || "RaolByte", 87 | error: "API key required", 88 | message: "Please provide a valid API key in the query parameters" 89 | }) 90 | } 91 | 92 | const settings = loadSettings() 93 | 94 | if (!settings || !settings.apiSettings || !settings.apiSettings.apikey) { 95 | return res.status(500).json({ 96 | status: false, 97 | creator: settings?.apiSettings?.creator || "RaolByte", 98 | error: "Server configuration error", 99 | message: "API key validation is not properly configured" 100 | }) 101 | } 102 | 103 | if (!settings.apiSettings.apikey[apikey]) { 104 | return res.status(403).json({ 105 | status: false, 106 | creator: settings?.apiSettings?.creator || "RaolByte", 107 | error: "Invalid API key", 108 | message: "The provided API key is not valid or does not exist" 109 | }) 110 | } 111 | 112 | if (!checkRateLimit(apikey)) { 113 | return res.status(429).json({ 114 | status: false, 115 | creator: settings?.apiSettings?.creator || "RaolByte", 116 | error: "Rate limit exceeded", 117 | message: "You have exceeded the rate limit for this API key" 118 | }) 119 | } 120 | 121 | next() 122 | } 123 | 124 | export function createApiKeyMiddleware() { 125 | return (req, res, next) => { 126 | const settings = loadSettings() 127 | 128 | if (!settings || !settings.apiSettings) { 129 | return next() 130 | } 131 | 132 | if (settings.apiSettings.requireApikey === false) { 133 | return next() 134 | } 135 | 136 | if (settings.apiSettings.requireApikey === true) { 137 | return validateApiKey(req, res, next) 138 | } 139 | 140 | return next() 141 | } 142 | } -------------------------------------------------------------------------------- /src/notifications.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "#01", 4 | "date": "01-07-2025", 5 | "title": "UI Interface Update", 6 | "message": "The interface has been updated to be more modern, responsive, and attractive to enhance user experience." 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /src/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Raol Api'S", 3 | "version": "v7.1.0", 4 | "description": "Simple and easy to use API documentation.", 5 | "maintenance": { 6 | "enabled": false, 7 | "message": "" 8 | }, 9 | "bannerImage": "/src/images/banner.jpg", 10 | "previewImage": "/src/images/preview.png", 11 | "header": { 12 | "status": "Online" 13 | }, 14 | "apiSettings": { 15 | "creator": "RaolByte", 16 | "requireApikey": false, 17 | "apikey": { 18 | "demo": { 19 | "rateLimit": "5000/day", 20 | "enabled": true 21 | }, 22 | "admin": { 23 | "rateLimit": "unlimited", 24 | "enabled": true 25 | }, 26 | "Raol": { 27 | "rateLimit": "unlimited", 28 | "enabled": true, 29 | "category": "premium", 30 | "name": "Raol" 31 | } 32 | } 33 | }, 34 | "categories": [ 35 | { 36 | "name": "Artificial Intelligence", 37 | "items": [ 38 | { 39 | "name": "LuminAI", 40 | "desc": "Advanced AI assistant powered by LuminAI for intelligent conversations", 41 | "path": "/ai/luminai?text=", 42 | "status": "error", 43 | "params": { 44 | "text": "Text message for the AI to respond to" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "name": "Random Images", 51 | "items": [ 52 | { 53 | "name": "Blue Archive", 54 | "desc": "Get random high-quality Blue Archive character images", 55 | "path": "/random/ba", 56 | "status": "ready" 57 | } 58 | ] 59 | }, 60 | { 61 | "name": "Image Makers", 62 | "items": [ 63 | { 64 | "name": "BRAT Generator", 65 | "desc": "Generate BRAT-style text images with customizable colors and backgrounds", 66 | "path": "/maker/brat?text=Hello World", 67 | "status": "ready", 68 | "params": { 69 | "text": "Text to be inserted into the BRAT image (required)" 70 | } 71 | }, 72 | { 73 | "name": "BRAT Video Generator", 74 | "desc": "Generate animated BRAT-style text videos with customizable colors and backgrounds", 75 | "path": "/maker/bratvid?text=Hello World", 76 | "status": "ready", 77 | "params": { 78 | "text": "Text to be inserted into the BRAT video (required)" 79 | } 80 | } 81 | ] 82 | } 83 | ], 84 | "apiKeys": [ 85 | { 86 | "key": "Raol", 87 | "name": "Raol", 88 | "category": "premium", 89 | "ratelimit": "unlimited", 90 | "active": true, 91 | "createdAt": "2025-10-05T04:45:58.171Z" 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /src/sponsor.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "title": "Sponsored Ads", 4 | "subtitle": "Support these amazing services ❤", 5 | "showOnLoad": true, 6 | "showInterval": 300000, 7 | "sponsors": [ 8 | { 9 | "id": "vgxteam", 10 | "name": "RaolByte", 11 | "logo": "", 12 | "backgroundColor": "#1a1a1a", 13 | "textColor": "#ffffff", 14 | "bannerImage": "/src/images/banner.jpg", 15 | "url": "https://whatsapp.com/channel/0029Vb6n9HIDJ6H6oibRvv1D", 16 | "active": true 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "index.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "/index.js" 13 | } 14 | ], 15 | "env": { 16 | "NODE_ENV": "production" 17 | } 18 | } 19 | --------------------------------------------------------------------------------