├── .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 |
5 |
6 | **Simple and easy to use API with Discord Bot Integration.**
7 |
8 | [](https://github.com/raolbyte/Raol-UI)
9 | [](https://nodejs.org/)
10 | [](LICENSE)
11 | [](https://raol-apis.vercel.app)
12 | [](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 |
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 |
74 |
75 |
76 |
77 |
Loading...
78 |
79 |
© 2025 Raol Api'S Corp. All rights reserved.
80 |
81 |
82 |
101 |
102 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
124 |
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 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
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 |
182 |
183 |
184 |
185 |
203 |
204 |
205 |
206 |
207 |
208 |
217 |
218 |
219 |
220 | Endpoint
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
Processing request...
238 |
239 |
240 |
241 |
242 | Response
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
267 |
268 | This is a notification message.
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
Important Notice!
280 |
If you find any bugs, suggestions, or feedback, please contact the admin
281 |
287 |
288 |
289 |
290 |
291 |
292 |
293 | Feedback
294 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
21 | Go Back
22 |
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 |
312 |
313 |
314 |
315 | Retry
316 |
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 |
--------------------------------------------------------------------------------