@dytsou/shorten-url 2.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dytsou
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
+
package/README.md ADDED
@@ -0,0 +1,359 @@
1
+ # URL Shortener
2
+
3
+ A modern, fast URL shortener built with Cloudflare Workers and a responsive web interface. This project allows anyone to create short URLs with optional custom slugs, featuring a clean UI and robust backend with comprehensive API documentation.
4
+
5
+ ## Features
6
+
7
+ - **URL Shortening**: Convert long URLs into short, shareable links
8
+ - **Custom Slugs**: Create personalized short URLs (optional)
9
+ - **Protected Shortening**: Short URL creation is protected behind Cloudflare Access / WARP for safer usage
10
+ - **Responsive Design**: Works seamlessly on desktop and mobile devices
11
+ - **Fast Performance**: Built on Cloudflare Workers for global edge deployment
12
+ - **Security Features**: URL validation, rate limiting, and optional Safe Browsing integration
13
+ - **Modern Copy Function**: One-click copying with fallbacks for all browsers
14
+ - **Custom Error Pages**: Beautiful 404, security warning, and redirect pages (fully customizable via your GitHub Pages)
15
+ - **API Documentation**: Interactive Swagger UI with OpenAPI 3.1.0 specification
16
+ - **Analytics Ready**: Optional click tracking and analytics integration
17
+
18
+ ## Quick Start
19
+
20
+ ### Prerequisites
21
+
22
+ - A Cloudflare account with Workers enabled
23
+ - GitHub account for hosting the frontend (or any static hosting)
24
+ - Basic knowledge of Git and command line
25
+
26
+ ### 1. Clone the Repository
27
+
28
+ ```bash
29
+ git clone https://github.com/dytsou/shorten-url.git
30
+ cd shorten-url
31
+ ```
32
+
33
+ ### 2. Setup Configuration
34
+
35
+ ```bash
36
+ # Copy the example configuration
37
+ cp config/config.example.js config/config.js
38
+
39
+ # Edit the configuration file
40
+ vim config/config.js
41
+ ```
42
+
43
+ Update the `config/config.js` file with your settings:
44
+
45
+ ```javascript
46
+ const config = {
47
+ frontend: {
48
+ // IMPORTANT: Update this to your GitHub Pages URL or custom domain
49
+ url: "https://yourusername.github.io/shorten-url/",
50
+ },
51
+ // ... other settings
52
+ };
53
+ ```
54
+
55
+ ### 3. Deploy Frontend
56
+
57
+ #### Option A: GitHub Pages (Recommended)
58
+
59
+ 1. Push your code to GitHub
60
+ 2. Go to your repository settings
61
+ 3. Enable GitHub Pages for the main branch
62
+ 4. Your frontend will be available at `https://yourusername.github.io/shorten-url/`
63
+
64
+ #### Option B: Custom Domain
65
+
66
+ 1. Upload the `docs/` directory to your web hosting
67
+ 2. Update the `frontend.url` in `config/config.js` to match your domain
68
+
69
+ ### 4. Setup Cloudflare Workers
70
+
71
+ #### Install Wrangler CLI
72
+
73
+ ```bash
74
+ npm install -g wrangler
75
+ ```
76
+
77
+ #### Configure Wrangler
78
+
79
+ ```bash
80
+ # Login to Cloudflare
81
+ wrangler auth login
82
+
83
+ # Create a new KV namespace for storing URLs
84
+ wrangler kv:namespace create "LINKS"
85
+ ```
86
+
87
+ #### Create wrangler.toml
88
+
89
+ Create a `wrangler.toml` file in your project root:
90
+
91
+ ```toml
92
+ name = "url-shortener"
93
+ main = "src/worker.js"
94
+ compatibility_date = "2024-01-01"
95
+
96
+ [[kv_namespaces]]
97
+ binding = "LINKS"
98
+ id = "your-kv-namespace-id-here"
99
+ ```
100
+
101
+ Replace `your-kv-namespace-id-here` with the ID from the KV namespace creation command.
102
+
103
+ #### Deploy the Worker
104
+
105
+ ```bash
106
+ wrangler deploy
107
+ ```
108
+
109
+ ### 5. Update Configuration
110
+
111
+ After deploying, update your `config/config.js` with the worker URL:
112
+
113
+ ```javascript
114
+ const config = {
115
+ frontend: {
116
+ url: "https://yourusername.github.io/shorten-url/",
117
+ },
118
+ // ... other settings
119
+ };
120
+ ```
121
+
122
+ ## ⚙️ Configuration Options
123
+
124
+ ### Frontend Configuration
125
+
126
+ | Option | Description | Default |
127
+ | ------------------------ | --------------------------------------- | -------- |
128
+ | `frontend.url` | URL where your frontend is hosted | Required |
129
+ | `frontend.displayDomain` | Domain shown in UI (null = auto-detect) | `null` |
130
+ | `frontend.theme` | UI theme selection | `""` |
131
+
132
+ ### Worker Configuration
133
+
134
+ | Option | Description | Default |
135
+ | ------------------------------- | --------------------------------- | ---------- |
136
+ | `worker.no_ref` | Hide HTTP referrer header | `"off"` |
137
+ | `worker.cors` | Enable CORS for API requests | `"on"` |
138
+ | `worker.unique_link` | Same URL = same short link | `true` |
139
+ | `worker.custom_link` | Allow custom slugs | `true` |
140
+ | `worker.safe_browsing_api_key` | Google Safe Browsing API key | `""` |
141
+ | `worker.min_random_key_length` | Minimum length for generated keys | `6` |
142
+ | `worker.max_custom_slug_length` | Maximum length for custom slugs | `50` |
143
+ | `worker.reserved_slugs` | List of reserved slugs | See config |
144
+
145
+ ### Security Configuration
146
+
147
+ | Option | Description | Default |
148
+ | -------------------------- | -------------------------- | ------- |
149
+ | `security.rate_limit` | Requests per minute per IP | `10` |
150
+ | `security.validate_urls` | Enable URL validation | `true` |
151
+ | `security.blocked_domains` | List of blocked domains | `[]` |
152
+
153
+ ### Storage Configuration
154
+
155
+ | Option | Description | Default |
156
+ | ---------------------- | ------------------------- | --------- |
157
+ | `storage.binding_name` | KV namespace binding name | `"LINKS"` |
158
+
159
+ ## 🔧 Advanced Setup
160
+
161
+ ### Custom Domain
162
+
163
+ 1. Add a custom domain in Cloudflare Workers dashboard
164
+ 2. Update your DNS records to point to Cloudflare
165
+ 3. Update the worker URL in your configuration
166
+
167
+ ### Google Safe Browsing
168
+
169
+ 1. Get an API key from [Google Cloud Console](https://developers.google.com/safe-browsing/v4/get-started)
170
+ 2. Add the key to your `config.js`:
171
+
172
+ ```javascript
173
+ worker: {
174
+ safe_browsing_api_key: "your-api-key-here",
175
+ }
176
+ ```
177
+
178
+ ### Analytics Integration
179
+
180
+ Configure analytics in your `config.js`:
181
+
182
+ ```javascript
183
+ analytics: {
184
+ enable_tracking: true,
185
+ ga_tracking_id: "G-XXXXXXXXXX", // Google Analytics 4
186
+ analytics_endpoint: "https://your-analytics-endpoint.com"
187
+ }
188
+ ```
189
+
190
+ ### Custom Error Pages
191
+
192
+ The system fetches error and redirect pages from your GitHub Pages repository, so you can fully customize their content and design:
193
+
194
+ - **404 Page**: <code>https://dytsou.github.io/404.html</code>
195
+ - **Security Warning**: <code>https://dytsou.github.io/safe-browsing-warning.html</code>
196
+ - **No-Referrer Redirect**: <code>https://dytsou.github.io/no-ref-page.html</code>
197
+
198
+ You can edit these HTML files in your GitHub Pages repo to change the look, text, or behavior at any time. The worker will always serve the latest version from your site.
199
+
200
+ All pages feature responsive design with glassmorphism effects matching your main interface.
201
+
202
+ ## 📖 API Documentation
203
+
204
+ The project includes comprehensive API documentation available at `/api` endpoint with interactive Swagger UI.
205
+
206
+ ### OpenAPI Specification
207
+
208
+ - **Format**: OpenAPI 3.1.0
209
+ - **Location**: [docs/api/openapi.yaml](docs/api/openapi.yaml)
210
+ - **Interactive UI**: Available at `/api` endpoint on your deployed worker
211
+ - **Offline Access**: [docs/api/index.html](docs/api/index.html) for local viewing
212
+
213
+ ### Shorten URL
214
+
215
+ **POST** `/shorten`
216
+
217
+ ```json
218
+ {
219
+ "url": "https://example.com/very-long-url",
220
+ "custom_slug": "my-link" // optional
221
+ }
222
+ ```
223
+
224
+ **Response:**
225
+
226
+ ```json
227
+ {
228
+ "short_url": "https://url.tsou.me/abc123"
229
+ }
230
+ ```
231
+
232
+ **Error Response:**
233
+
234
+ ```json
235
+ {
236
+ "status": 400,
237
+ "message": "Invalid URL format"
238
+ }
239
+ ```
240
+
241
+ ### Access Short URL
242
+
243
+ **GET** `/{key}`
244
+
245
+ Redirects to the original URL or shows appropriate error page.
246
+
247
+ ### Additional Endpoints
248
+
249
+ - **GET** `/api` - Interactive API documentation (Swagger UI)
250
+
251
+ ## 🛠️ Development
252
+
253
+ ### Local Development
254
+
255
+ ```bash
256
+ # Start local development server (see wrangler.toml for port / host)
257
+ wrangler dev
258
+
259
+ # Test with curl (example; in production, requests must pass through Cloudflare Access/WARP)
260
+ curl -X POST http://localhost:8787/shorten \
261
+ -H "Content-Type: application/json" \
262
+ -d '{"url": "https://example.com"}'
263
+ ```
264
+
265
+ ### File Structure
266
+
267
+ ```
268
+ shorten-url/
269
+ ├── src/
270
+ │ ├── worker.js # Production Cloudflare Worker
271
+ │ ├── worker.dev.js # Development Cloudflare Worker
272
+ │ └── worker.example.js # Worker template
273
+ ├── docs/
274
+ │ ├── index.html # Homepage interface
275
+ │ └── api/
276
+ │ ├── index.html # Swagger UI for API docs
277
+ │ └── openapi.yaml # OpenAPI 3.1.0 specification
278
+ ├── config/
279
+ │ ├── config.js # Configuration (not in git)
280
+ │ └── config.example.js # Configuration template
281
+ ├── .cursor/ # Cursor IDE rules and workflow
282
+ ├── .github/ # GitHub workflows and templates
283
+ ├── .gitignore # Git ignore rules
284
+ ├── README.md # This file
285
+ └── wrangler.toml # Wrangler configuration
286
+ ```
287
+
288
+ ## Security Considerations
289
+
290
+ - Keep your `config.js` file private (it's in `.gitignore`)
291
+ - Use environment variables for sensitive data in production
292
+ - Enable rate limiting to prevent abuse
293
+ - Consider enabling Google Safe Browsing for malicious URL detection
294
+ - Regularly monitor your KV storage usage
295
+ - Configure Cloudflare Access / WARP correctly, as URL shortening is intended to be available only to authenticated/requesters you control
296
+
297
+ ## Troubleshooting
298
+
299
+ ### Common Issues
300
+
301
+ **"Failed to copy" error:**
302
+
303
+ - The app uses modern clipboard API with fallbacks
304
+ - Ensure you're using HTTPS (required for clipboard access)
305
+
306
+ **Worker deployment fails:**
307
+
308
+ - Check your `wrangler.toml` configuration
309
+ - Ensure KV namespace ID is correct
310
+ - Verify you're logged into the correct Cloudflare account
311
+
312
+ **Custom slugs not working:**
313
+
314
+ - Check that `custom_link` is enabled in config
315
+ - Verify slug meets validation requirements (alphanumeric, hyphens, underscores only)
316
+ - Ensure slug is not in the reserved slugs list
317
+
318
+ **Frontend not loading:**
319
+
320
+ - Update `frontend.url` in your config to match your hosting URL
321
+ - Ensure CORS is enabled in worker configuration
322
+
323
+ **Rate limiting issues:**
324
+
325
+ - Adjust `security.rate_limit` in your configuration
326
+ - Consider implementing IP allowlisting for trusted sources
327
+
328
+ ## License
329
+
330
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
331
+
332
+ ## Contributing
333
+
334
+ 1. Fork the repository
335
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
336
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
337
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
338
+ 5. Open a Pull Request
339
+
340
+ ## Support
341
+
342
+ If you encounter any issues or have questions:
343
+
344
+ 1. Check the [troubleshooting section](#-troubleshooting)
345
+ 2. Search existing issues on GitHub
346
+ 3. Create a new issue with detailed information
347
+
348
+ ## Deployment Checklist
349
+
350
+ - [ ] Updated `config.js` with your settings
351
+ - [ ] Deployed frontend to GitHub Pages or custom hosting
352
+ - [ ] Created Cloudflare KV namespace
353
+ - [ ] Configured `wrangler.toml` with correct KV namespace ID
354
+ - [ ] Deployed worker using `wrangler deploy`
355
+ - [ ] Tested URL shortening functionality
356
+ - [ ] Verified copy-to-clipboard feature works
357
+ - [ ] Tested custom error pages (404, security warnings)
358
+ - [ ] (Optional) Configured custom domain
359
+ - [ ] (Optional) Added Google Safe Browsing API key
@@ -0,0 +1,118 @@
1
+ /**
2
+ * URL Shortener Configuration Example
3
+ *
4
+ * Copy this file to config.js and modify the values according to your setup.
5
+ * This file should be committed to your repository as a template.
6
+ * The actual config.js file should be added to .gitignore to keep your secrets safe.
7
+ */
8
+
9
+ const config = {
10
+ // Frontend Configuration
11
+ frontend: {
12
+ // URL where your frontend is hosted (used by worker to serve the main page)
13
+ // REQUIRED: Change this to your GitHub Pages URL or custom domain
14
+ url: "https://yourusername.github.io/your-repo-name/",
15
+
16
+ // Domain displayed in the custom slug input prefix
17
+ // If null, will use the current domain automatically
18
+ // Example: "short.ly" or "yourdomain.com"
19
+ displayDomain: null,
20
+
21
+ // Default theme for the frontend
22
+ theme: "", // Use empty string for default theme, or "theme/urlcool" for urlcool theme
23
+ },
24
+
25
+ // Worker Configuration
26
+ worker: {
27
+ // Control the HTTP referrer header
28
+ // Set to "on" to create anonymous links that hide the HTTP Referer header
29
+ no_ref: "off",
30
+
31
+ // Allow Cross-origin resource sharing for API requests
32
+ cors: "on",
33
+
34
+ // If true, the same long URL will be shortened into the same short URL
35
+ unique_link: true,
36
+
37
+ // Allow users to customize the short URL
38
+ custom_link: true,
39
+
40
+ // Google Safe Browsing API Key for URL safety check before redirect
41
+ // Get your API key from: https://developers.google.com/safe-browsing/v4/get-started
42
+ // Leave empty to disable safety checking
43
+ safe_browsing_api_key: "",
44
+
45
+ // Maximum length for custom slugs
46
+ max_custom_slug_length: 50,
47
+
48
+ // Minimum length for generated random keys
49
+ min_random_key_length: 6,
50
+
51
+ // Characters used for random key generation (removed confusing characters)
52
+ random_chars: "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
53
+
54
+ // Reserved slugs that cannot be used as custom URLs
55
+ reserved_slugs: [
56
+ "api",
57
+ "admin",
58
+ "www",
59
+ "mail",
60
+ "ftp",
61
+ "localhost",
62
+ "password",
63
+ "help",
64
+ "support",
65
+ "contact",
66
+ "about",
67
+ ],
68
+ },
69
+
70
+ // KV Storage Configuration (Cloudflare Workers)
71
+ storage: {
72
+ // KV namespace binding name (must match wrangler.toml)
73
+ binding_name: "LINKS",
74
+ },
75
+
76
+ // Security Configuration
77
+ security: {
78
+ // Rate limiting (requests per minute per IP)
79
+ rate_limit: 10,
80
+
81
+ // Enable URL validation
82
+ validate_urls: true,
83
+
84
+ // Block suspicious domains
85
+ block_suspicious_domains: true,
86
+
87
+ // List of blocked domains (exact matches)
88
+ // Add domains you want to block from being shortened
89
+ blocked_domains: [
90
+ // "bit.ly",
91
+ // "tinyurl.com",
92
+ // "t.co"
93
+ ],
94
+ },
95
+
96
+ // Analytics Configuration (optional)
97
+ analytics: {
98
+ // Enable click tracking
99
+ enable_tracking: false,
100
+
101
+ // Google Analytics tracking ID (GA4)
102
+ // Example: "G-XXXXXXXXXX"
103
+ ga_tracking_id: "",
104
+
105
+ // Custom analytics endpoint for tracking clicks
106
+ analytics_endpoint: "",
107
+ },
108
+ };
109
+
110
+ // Export for use in other files
111
+ if (typeof module !== "undefined" && module.exports) {
112
+ module.exports = config;
113
+ }
114
+
115
+ // Make available globally for browser usage
116
+ if (typeof window !== "undefined") {
117
+ window.APP_CONFIG = config;
118
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * URL Shortener Configuration
3
+ *
4
+ * This file contains all configurable settings for the URL shortener.
5
+ * Copy this file from config.example.js and modify the values as needed.
6
+ */
7
+
8
+ const config = {
9
+ // Frontend Configuration
10
+ frontend: {
11
+ // URL where your frontend is hosted (used by worker to serve the main page)
12
+ url: "https://dytsou.github.io/shorten-url/",
13
+
14
+ // Domain displayed in the custom slug input prefix
15
+ // If null, will use the current domain automatically
16
+ displayDomain: null,
17
+
18
+ // Default theme for the frontend
19
+ theme: "", // Use empty string for default theme, or "theme/urlcool" for urlcool theme
20
+ },
21
+
22
+ // Worker Configuration
23
+ worker: {
24
+ // Control the HTTP referrer header
25
+ // Set to "on" to create anonymous links that hide the HTTP Referer header
26
+ no_ref: "off",
27
+
28
+ // Allow Cross-origin resource sharing for API requests
29
+ cors: "on",
30
+
31
+ // If true, the same long URL will be shortened into the same short URL
32
+ unique_link: true,
33
+
34
+ // Allow users to customize the short URL
35
+ custom_link: true,
36
+
37
+ // Google Safe Browsing API Key for URL safety check before redirect
38
+ // Leave empty to disable safety checking
39
+ safe_browsing_api_key: "",
40
+
41
+ // Maximum length for custom slugs
42
+ max_custom_slug_length: 50,
43
+
44
+ // Minimum length for generated random keys
45
+ min_random_key_length: 6,
46
+
47
+ // Characters used for random key generation (removed confusing characters)
48
+ random_chars: 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678',
49
+
50
+ // Reserved slugs that cannot be used as custom URLs
51
+ reserved_slugs: ['api', 'admin', 'www', 'mail', 'ftp', 'localhost', 'password', 'help', 'support', 'contact', 'about']
52
+ },
53
+
54
+ // KV Storage Configuration (Cloudflare Workers)
55
+ storage: {
56
+ // KV namespace binding name (must match wrangler.toml)
57
+ binding_name: "LINKS"
58
+ },
59
+
60
+ // Security Configuration
61
+ security: {
62
+ // Rate limiting (requests per minute per IP)
63
+ rate_limit: 10,
64
+
65
+ // Enable URL validation
66
+ validate_urls: true,
67
+
68
+ // Block suspicious domains
69
+ block_suspicious_domains: true,
70
+
71
+ // List of blocked domains (exact matches)
72
+ blocked_domains: [
73
+ "bit.ly",
74
+ "tinyurl.com",
75
+ "t.co",
76
+ "reurl.cc"
77
+ ]
78
+ },
79
+
80
+ // Analytics Configuration (optional)
81
+ analytics: {
82
+ // Enable click tracking
83
+ enable_tracking: false,
84
+
85
+ // Google Analytics tracking ID
86
+ ga_tracking_id: "",
87
+
88
+ // Custom analytics endpoint
89
+ analytics_endpoint: ""
90
+ }
91
+ };
92
+
93
+ // Export for use in other files
94
+ if (typeof module !== 'undefined' && module.exports) {
95
+ module.exports = config;
96
+ }
97
+
98
+ // Make available globally for browser usage
99
+ if (typeof window !== 'undefined') {
100
+ window.APP_CONFIG = config;
101
+ }
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Shorten URL API</title>
6
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
7
+ <style>
8
+ html,
9
+ body {
10
+ height: 100%;
11
+ margin: 0;
12
+ }
13
+
14
+ #swagger-ui {
15
+ height: 100vh;
16
+ }
17
+ </style>
18
+ </head>
19
+
20
+ <body>
21
+ <div id="swagger-ui"></div>
22
+ <script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
23
+ <script>
24
+ window.onload = () => {
25
+ SwaggerUIBundle({
26
+ url: "openapi.yaml",
27
+ dom_id: "#swagger-ui",
28
+ presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
29
+ layout: "BaseLayout",
30
+ });
31
+ };
32
+ </script>
33
+ </body>
34
+ </html>