@dytsou/shorten-url 2.4.4 → 2.4.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/README.md +88 -50
- package/config/config.example.js +137 -0
- package/config/config.js +19 -0
- package/docs/about.md +360 -0
- package/docs/api/openapi.yaml +441 -81
- package/docs/flagship.md +58 -0
- package/docs/plans/2026-09-06-001-refactor-worker-hosted-react-assets-plan.md +308 -0
- package/frontend/about-template.html +124 -0
- package/frontend/index.html +17 -0
- package/frontend/package.json +20 -0
- package/frontend/pnpm-lock.yaml +1166 -0
- package/frontend/pnpm-workspace.yaml +5 -0
- package/frontend/src/App.jsx +1264 -0
- package/frontend/src/api.js +272 -0
- package/frontend/src/local-flag-mock.js +55 -0
- package/frontend/src/main.jsx +4 -0
- package/frontend/src/styles/app.css +2253 -0
- package/frontend/static/favicon.svg +5 -0
- package/frontend/vite.config.js +34 -0
- package/package.json +6 -4
- package/src/lib/access.js +241 -1
- package/src/lib/assets.js +58 -0
- package/src/lib/endpoints.js +33 -8
- package/src/lib/feature-toggle.js +464 -0
- package/src/lib/flag-management.js +137 -0
- package/src/lib/flag-routes.js +155 -0
- package/src/lib/flagship.js +269 -0
- package/src/lib/observability.js +34 -0
- package/src/lib/responses.js +39 -3
- package/src/lib/runtime-config.js +90 -0
- package/src/lib/safety.js +15 -0
- package/src/lib/shortener.js +2 -2
- package/src/lib/worker-handler.js +122 -0
- package/src/worker.js +3 -66
package/docs/about.md
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# URL Shortener
|
|
2
|
+
|
|
3
|
+
A URL shortener built with Cloudflare Workers, Cloudflare KV, and a responsive React interface. The Worker serves the product UI and redirects, while `docs/` contains the documentation and OpenAPI reference.
|
|
4
|
+
|
|
5
|
+
[](https://cloudflare.com)
|
|
6
|
+
[](https://deploy.workers.cloudflare.com/?url=https://github.com/dytsou/shorten-url)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **URL Shortening**: Create short links stored in Cloudflare KV and redirect with HTTP 302
|
|
11
|
+
- **Custom Slugs**: Optionally choose a slug using letters, numbers, hyphens, or underscores
|
|
12
|
+
- **Cloudflare Access**: The Worker checks for Cloudflare Access JWT and email headers before creating links
|
|
13
|
+
- **Responsive Design**: Worker-hosted React interface supports desktop and mobile screens
|
|
14
|
+
- **Flagship Controls**: Shorten and protected Flagship views share one interface
|
|
15
|
+
- **Unique Links**: Optionally reuse the same short key when the destination URL already exists
|
|
16
|
+
- **URL Safety**: Validate submitted URLs and optionally check redirect destinations with Google Safe Browsing
|
|
17
|
+
- **Copy Support**: Copy shortened URLs with a manual-selection fallback
|
|
18
|
+
- **Hosted Error Pages**: Configure external 404, error, Safe Browsing warning, and no-referrer pages
|
|
19
|
+
- **API Documentation**: Static Swagger UI with an OpenAPI 3.1 specification
|
|
20
|
+
- **Worker Observability**: Structured logs and traces are supported
|
|
21
|
+
|
|
22
|
+
The Worker does not implement click analytics, per-IP rate limiting, or blocked-domain lists. The similarly named settings in `config/config.js` are not used by the current Worker.
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Worker-hosted React frontend
|
|
27
|
+
|
|
28
|
+
The product UI is React source in `frontend/`, compiled to `frontend/dist`, and served by the Worker through its `ASSETS` binding. Worker routing and APIs remain in `src/`; the API reference from `docs/api/` is bundled into Worker assets at `/api/` and `/api/openapi.yaml`. The separate Cloudflare Pages site can publish the documentation landing page from `docs/`; `docs/index.html` is not a product asset.
|
|
29
|
+
|
|
30
|
+
For a clean local setup, install both declared packages and use the root commands:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm install --frozen-lockfile --ignore-scripts
|
|
34
|
+
pnpm --dir frontend install --frozen-lockfile --ignore-scripts
|
|
35
|
+
cp wrangler.toml.example wrangler.toml
|
|
36
|
+
# Set a real LINKS namespace and production-only Access values in wrangler.toml or secrets.
|
|
37
|
+
pnpm build
|
|
38
|
+
pnpm dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`pnpm dev`, `pnpm preview`, and `pnpm deploy` build `frontend/dist` before running Wrangler. The Worker serves the UI at `GET /`. Only the `production` branch mounts its product page at `/shorten`; other branch previews stay at `/`. Regardless of the page path, the UI sends API requests to `POST /` and `/settings/api/*`. Standalone settings pages are not exposed.
|
|
42
|
+
|
|
43
|
+
Production must use an Access-protected custom hostname, set `workers_dev = false`, and set `ACCESS_ALLOWED_HOSTS` to that hostname. Keep `CLOUDFLARE_API_TOKEN`, `FLAGSHIP_CSRF_SECRET`, and provider credentials in Cloudflare/GitHub secrets, never Vite metadata or tracked configuration. Optional `FRONTEND_URL` and `FRONTEND_PAGES_BASE` retain legacy error/interstitial pages only; they are not used to serve the normal UI.
|
|
44
|
+
|
|
45
|
+
The Worker deployment workflow reads the non-secret GitHub variables
|
|
46
|
+
`LINKS_KV_NAMESPACE_ID` and `ACCESS_ALLOWED_HOSTS`, plus the
|
|
47
|
+
`CLOUDFLARE_API_TOKEN` secret, to create its ignored `wrangler.toml` at build
|
|
48
|
+
time.
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
- A Cloudflare account with Workers and Pages; the Deploy to Cloudflare button provisions the required KV namespace
|
|
53
|
+
- A GitHub or GitLab account to receive the repository created by the Deploy to Cloudflare button
|
|
54
|
+
- A Cloudflare Pages project to publish `docs/`
|
|
55
|
+
- Node.js 24 or newer and pnpm 10.22.0 for manual deployment
|
|
56
|
+
|
|
57
|
+
### 1. Deploy with Cloudflare (Recommended)
|
|
58
|
+
|
|
59
|
+
1. Click the **Deploy to Cloudflare** button near the top of this README.
|
|
60
|
+
2. Connect your Cloudflare and GitHub or GitLab accounts, then choose the destination repository and Worker name.
|
|
61
|
+
3. Confirm the `LINKS` KV binding. Cloudflare provisions the namespace and deploys the Worker from the `main` branch.
|
|
62
|
+
|
|
63
|
+
The button deploys the Worker; it does not publish `docs/`. Connect the created repository to Cloudflare Pages, leave the build command blank, and set the output directory to `docs`. The React product UI is built into `frontend/dist` and served by the Worker through `ASSETS`; use the Worker deployment workflow or `pnpm deploy` to build and deploy those assets.
|
|
64
|
+
|
|
65
|
+
### 2. Alternative: Manual Worker Deployment
|
|
66
|
+
|
|
67
|
+
Clone the repository and install the pinned Wrangler version:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/dytsou/shorten-url.git
|
|
71
|
+
cd shorten-url
|
|
72
|
+
corepack enable
|
|
73
|
+
pnpm install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The Worker serves the product UI from `ASSETS`. To deploy manually, install both packages, create the local Wrangler file from the tracked example, set the `LINKS` namespace ID and Access hostname, then build and deploy:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pnpm install --frozen-lockfile --ignore-scripts
|
|
80
|
+
pnpm --dir frontend install --frozen-lockfile --ignore-scripts
|
|
81
|
+
cp wrangler.toml.example wrangler.toml
|
|
82
|
+
# Set the LINKS namespace ID and ACCESS_ALLOWED_HOSTS.
|
|
83
|
+
pnpm build
|
|
84
|
+
pnpm exec wrangler login
|
|
85
|
+
pnpm deploy
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The `LINKS` binding in `wrangler.toml` omits an account-specific namespace ID so Wrangler can provision it. To use an existing KV namespace, create it with `pnpm exec wrangler kv namespace create LINKS` and set its ID in `wrangler.toml` before deploying.
|
|
89
|
+
|
|
90
|
+
### 3. Deploy Documentation to Cloudflare Pages
|
|
91
|
+
|
|
92
|
+
The `docs/` directory contains the API reference and documentation. To create a Pages site, connect your repository, set the production branch to the branch containing your documentation, leave the build command blank, and use `docs` as the output directory.
|
|
93
|
+
|
|
94
|
+
This repository's Cloudflare Pages project publishes `docs/` from the `production` branch. The product UI is served by the Worker and does not depend on the Pages site.
|
|
95
|
+
|
|
96
|
+
For custom error and interstitial pages, set `frontend.pagesBase` to the directory containing those files.
|
|
97
|
+
|
|
98
|
+
The Worker returns short URLs using the request's origin, so configure its public domain in Cloudflare separately from `frontend.url`.
|
|
99
|
+
|
|
100
|
+
## ⚙️ Configuration Options
|
|
101
|
+
|
|
102
|
+
### Frontend Configuration
|
|
103
|
+
|
|
104
|
+
| Option | Description | Default |
|
|
105
|
+
| ------------------------ | ------------------------------------------------------- | ------- |
|
|
106
|
+
| `frontend.url` | Optional origin for legacy error and interstitial pages | `""` |
|
|
107
|
+
| `frontend.pagesBase` | Base URL for hosted error and interstitial pages | `url` |
|
|
108
|
+
| `frontend.workerOrigin` | Public Worker origin for a separately hosted frontend | `""` |
|
|
109
|
+
| `frontend.displayDomain` | Domain shown in UI (null = auto-detect) | `null` |
|
|
110
|
+
| `frontend.theme` | UI theme selection | `""` |
|
|
111
|
+
|
|
112
|
+
### Worker Configuration
|
|
113
|
+
|
|
114
|
+
| Option | Description | Default |
|
|
115
|
+
| ------------------------------- | --------------------------------------------------------------- | ---------- |
|
|
116
|
+
| `worker.no_ref` | Use the hosted no-referrer interstitial before redirecting | `"off"` |
|
|
117
|
+
| `worker.cors` | Add wildcard CORS headers for API responses | `"on"` |
|
|
118
|
+
| `worker.unique_link` | Reuse a key when the same URL has already been shortened | `true` |
|
|
119
|
+
| `worker.custom_link` | Allow custom slugs | `true` |
|
|
120
|
+
| `SAFE_BROWSING_API_KEY` | Optional Google Safe Browsing API key stored as a Worker secret | Unset |
|
|
121
|
+
| `worker.min_random_key_length` | Minimum length for generated keys | `6` |
|
|
122
|
+
| `worker.random_chars` | Characters used for generated keys | See config |
|
|
123
|
+
| `worker.max_custom_slug_length` | Maximum length for custom slugs | `50` |
|
|
124
|
+
| `worker.reserved_slugs` | Slugs that cannot be used as custom links | See config |
|
|
125
|
+
|
|
126
|
+
### Security Configuration
|
|
127
|
+
|
|
128
|
+
| Option | Description | Default |
|
|
129
|
+
| ----------------------------------- | ------------------------------------------------------------------ | ------- |
|
|
130
|
+
| `security.rate_limit` | Not implemented by the Worker | Unused |
|
|
131
|
+
| `security.validate_urls` | URLs are always validated; this setting does not toggle validation | Unused |
|
|
132
|
+
| `security.blocked_domains` | Domain blocklists are not implemented by the Worker | Unused |
|
|
133
|
+
| `security.block_suspicious_domains` | Domain blocklists are not implemented by the Worker | Unused |
|
|
134
|
+
|
|
135
|
+
### Storage Configuration
|
|
136
|
+
|
|
137
|
+
| Option | Description | Default |
|
|
138
|
+
| ---------------------- | -------------------------------------------------------------------- | --------- |
|
|
139
|
+
| `storage.binding_name` | Worker reads KV from `env.LINKS`; this config value is not consulted | `"LINKS"` |
|
|
140
|
+
|
|
141
|
+
## 🔧 Advanced Setup
|
|
142
|
+
|
|
143
|
+
### Custom Domain
|
|
144
|
+
|
|
145
|
+
1. Add a custom domain to the Cloudflare Worker and protect it with Cloudflare Access.
|
|
146
|
+
2. Add a custom domain to the Cloudflare Pages project for documentation.
|
|
147
|
+
3. Set `frontend.url` only when using separately hosted legacy error or interstitial pages.
|
|
148
|
+
|
|
149
|
+
### Google Safe Browsing
|
|
150
|
+
|
|
151
|
+
1. Get an API key from [Google Cloud Console](https://developers.google.com/safe-browsing/v4/get-started)
|
|
152
|
+
2. Store it as a Worker secret:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pnpm exec wrangler secret put SAFE_BROWSING_API_KEY
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The Worker reads this secret at runtime. The key is not stored in `config/config.js`.
|
|
159
|
+
|
|
160
|
+
### Analytics Integration
|
|
161
|
+
|
|
162
|
+
Click analytics is not implemented, and the `analytics` object in `config/config.js` is not used by the Worker. Cloudflare Worker logs and traces are enabled in `wrangler.toml`.
|
|
163
|
+
|
|
164
|
+
### Custom Error Pages
|
|
165
|
+
|
|
166
|
+
The Worker fetches optional error and interstitial pages from `frontend.pagesBase` (or `frontend.url` when `pagesBase` is not set). Add these files to your static host to customize them:
|
|
167
|
+
|
|
168
|
+
- **404 Page**: `404.html`
|
|
169
|
+
- **Error Page**: `error.html`
|
|
170
|
+
- **Security Warning**: `safe-browsing-warning.html`
|
|
171
|
+
- **No-Referrer Redirect**: `no-ref-page.html`
|
|
172
|
+
|
|
173
|
+
These files are not included in this repository. The Worker fetches their current contents from your static host when needed.
|
|
174
|
+
|
|
175
|
+
## 📖 API Documentation
|
|
176
|
+
|
|
177
|
+
The API reference is a static Swagger UI built from `docs/api/` and bundled into Worker assets. `/api` redirects to `/api/`, where the interactive reference is served.
|
|
178
|
+
|
|
179
|
+
### OpenAPI Specification
|
|
180
|
+
|
|
181
|
+
- **Format**: OpenAPI 3.1.0
|
|
182
|
+
- **Location**: [docs/api/openapi.yaml](/api/openapi.yaml)
|
|
183
|
+
- **Interactive UI**: [docs/api/index.html](/api/), bundled into Worker assets and served at `/api/`
|
|
184
|
+
- **Offline Access**: [docs/api/index.html](/api/) for local viewing
|
|
185
|
+
|
|
186
|
+
### Shorten URL
|
|
187
|
+
|
|
188
|
+
**POST** `/`
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"url": "https://example.com/very-long-url",
|
|
193
|
+
"custom_slug": "my-link"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The endpoint stays at the origin root even when the production page is mounted at `/shorten`. `custom_slug` is optional. The default Worker template requires Cloudflare Access headers before creating a link.
|
|
198
|
+
|
|
199
|
+
**Response (201 Created):**
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"short_url": "https://your-worker-domain.example/abc123"
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Error Response:**
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"status": 400,
|
|
212
|
+
"message": "Invalid URL format"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Access Short URL
|
|
217
|
+
|
|
218
|
+
**GET** `/{key}`
|
|
219
|
+
|
|
220
|
+
Redirects to the stored destination with HTTP 302. Query parameters from the short URL are appended to the destination.
|
|
221
|
+
|
|
222
|
+
### Additional Endpoints
|
|
223
|
+
|
|
224
|
+
- **GET** `/` - Serves the Worker-hosted React interface
|
|
225
|
+
- **OPTIONS** - Returns a CORS preflight response
|
|
226
|
+
|
|
227
|
+
### Flagship Management API
|
|
228
|
+
|
|
229
|
+
The authenticated control plane manages the `shorten-routing` flag under `/settings/api`. Requests require Cloudflare Access authentication. Fetch a CSRF token before sending a write; POST and PUT requests must include that token in `X-CSRF-Token`, use `application/json`, and come from the same origin as the UI.
|
|
230
|
+
|
|
231
|
+
| Method | Path | Purpose |
|
|
232
|
+
| ------ | -------------------------------------------- | --------------------------------------------------------- |
|
|
233
|
+
| `GET` | `/settings/api/csrf` | Get a CSRF token for the signed-in Access user and origin |
|
|
234
|
+
| `GET` | `/settings/api/flags` | Read the saved shortening flag |
|
|
235
|
+
| `POST` | `/settings/api/flag` | Create the `shorten-routing` flag |
|
|
236
|
+
| `PUT` | `/settings/api/flag/shorten-routing` | Save a draft with `expectedUpdatedAt` concurrency control |
|
|
237
|
+
| `POST` | `/settings/api/flag/shorten-routing/publish` | Publish the saved version |
|
|
238
|
+
|
|
239
|
+
Targeting rules are evaluated by ascending priority. Conditions in a rule are combined with AND and can target country, region, continent, timezone, browser language, or UTM source, medium, and campaign. Supported operators are `equals`, `not_equals`, `contains`, `starts_with`, `ends_with`, `in`, and `not_in`. A rule can also set `rolloutPercentage` from 0 to 100; the Worker uses a stable visitor key for the canary assignment. See the OpenAPI schema for request and response shapes.
|
|
240
|
+
|
|
241
|
+
## 🛠️ Development
|
|
242
|
+
|
|
243
|
+
### Local Development
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Start local development server (see wrangler.toml for port / host)
|
|
247
|
+
pnpm dev
|
|
248
|
+
|
|
249
|
+
# Local-only example. The handler checks for both headers; Cloudflare Access must
|
|
250
|
+
# authenticate production requests before they reach the Worker.
|
|
251
|
+
curl -X POST http://localhost:8787/ \
|
|
252
|
+
-H "Content-Type: application/json" \
|
|
253
|
+
-H "Cf-Access-Jwt-Assertion: local-test" \
|
|
254
|
+
-H "Cf-Access-Authenticated-User-Email: you@example.com" \
|
|
255
|
+
-d '{"url": "https://example.com"}'
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The Worker combines built-in defaults with non-secret Wrangler variables; keep credentials in Wrangler secrets. Run `pnpm test` to execute the Worker tests.
|
|
259
|
+
|
|
260
|
+
### File Structure
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
shorten-url/
|
|
264
|
+
├── src/
|
|
265
|
+
│ ├── lib/ # Validation, routes, KV, assets, and observability
|
|
266
|
+
│ ├── worker.js # Production Worker entrypoint
|
|
267
|
+
│ └── worker.example.js # Example Worker entrypoint
|
|
268
|
+
├── frontend/ # React product UI, about-page template, and build configuration
|
|
269
|
+
├── docs/
|
|
270
|
+
│ ├── index.html # Documentation site homepage
|
|
271
|
+
│ ├── about.md # Content rendered as the /about website page
|
|
272
|
+
│ └── api/
|
|
273
|
+
│ ├── index.html # Swagger UI for API docs
|
|
274
|
+
│ └── openapi.yaml # OpenAPI 3.1.0 specification
|
|
275
|
+
├── config/
|
|
276
|
+
│ ├── config.js # Existing deployment template
|
|
277
|
+
│ └── config.example.js # Configuration example
|
|
278
|
+
├── wrangler.toml.example # Wrangler deployment template
|
|
279
|
+
├── .github/ # GitHub workflows and templates
|
|
280
|
+
├── .gitignore # Ignores local-only files
|
|
281
|
+
└── README.md # This file
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Security Considerations
|
|
285
|
+
|
|
286
|
+
- Keep API keys out of version control; store them with Wrangler secrets
|
|
287
|
+
- Configure a Cloudflare Access policy in front of the Worker; the handler checks for Access headers but does not verify JWT signatures itself
|
|
288
|
+
- The Worker has no built-in per-IP rate limit or blocked-domain list
|
|
289
|
+
- Consider enabling Google Safe Browsing for redirect destinations
|
|
290
|
+
- Regularly monitor your KV storage usage
|
|
291
|
+
|
|
292
|
+
<a id="troubleshooting"></a>
|
|
293
|
+
|
|
294
|
+
## Troubleshooting
|
|
295
|
+
|
|
296
|
+
### Common Issues
|
|
297
|
+
|
|
298
|
+
**"Failed to copy" error:**
|
|
299
|
+
|
|
300
|
+
- The app uses modern clipboard API with fallbacks
|
|
301
|
+
- Ensure you're using HTTPS (required for clipboard access)
|
|
302
|
+
|
|
303
|
+
**Worker deployment fails:**
|
|
304
|
+
|
|
305
|
+
- Confirm `src/worker.js`, `wrangler.toml.example`, and `frontend/` are present
|
|
306
|
+
- Build the frontend so `frontend/dist/index.html` exists
|
|
307
|
+
- Ensure the `LINKS` KV namespace ID in `wrangler.toml` is valid
|
|
308
|
+
- Ensure KV namespace ID is correct
|
|
309
|
+
- Verify you're logged into the correct Cloudflare account
|
|
310
|
+
|
|
311
|
+
**Custom slugs not working:**
|
|
312
|
+
|
|
313
|
+
- Check that `custom_link` is enabled in config
|
|
314
|
+
- Verify slug meets validation requirements (alphanumeric, hyphens, underscores only)
|
|
315
|
+
- Ensure slug is not in the reserved slugs list
|
|
316
|
+
|
|
317
|
+
**Frontend not loading:**
|
|
318
|
+
|
|
319
|
+
- Check that the Worker has an `ASSETS` binding and `frontend/dist/index.html` was built
|
|
320
|
+
- If shortening returns an invalid-path response, check that the frontend POST path is `/`
|
|
321
|
+
- Enable CORS if the frontend and Worker use different origins
|
|
322
|
+
|
|
323
|
+
**Rate limiting issues:**
|
|
324
|
+
|
|
325
|
+
- The Worker does not currently rate-limit requests. Protect shortening with Cloudflare Access or add a rate limiter.
|
|
326
|
+
|
|
327
|
+
## License
|
|
328
|
+
|
|
329
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/dytsou/shorten-url/blob/main/LICENSE) file for details.
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
1. Fork the repository
|
|
334
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
335
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
336
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
337
|
+
5. Open a Pull Request
|
|
338
|
+
|
|
339
|
+
## Support
|
|
340
|
+
|
|
341
|
+
If you encounter any issues or have questions:
|
|
342
|
+
|
|
343
|
+
1. Check the [troubleshooting section](#troubleshooting)
|
|
344
|
+
2. Search existing issues on GitHub
|
|
345
|
+
3. Create a new issue with detailed information
|
|
346
|
+
|
|
347
|
+
## Deployment Checklist
|
|
348
|
+
|
|
349
|
+
- [ ] Set `LINKS_KV_NAMESPACE_ID` and `ACCESS_ALLOWED_HOSTS` for Worker deployment
|
|
350
|
+
- [ ] Build the React frontend into `frontend/dist`
|
|
351
|
+
- [ ] Deployed `docs/` to Cloudflare Pages
|
|
352
|
+
- [ ] Created Cloudflare KV namespace
|
|
353
|
+
- [ ] Configured `wrangler.toml` with correct KV namespace ID
|
|
354
|
+
- [ ] Protected the Worker with a Cloudflare Access policy
|
|
355
|
+
- [ ] Deployed the Worker using `pnpm deploy`
|
|
356
|
+
- [ ] Tested URL shortening at `POST /`
|
|
357
|
+
- [ ] Verified copy-to-clipboard feature works
|
|
358
|
+
- [ ] Tested custom error pages (404, security warnings)
|
|
359
|
+
- [ ] (Optional) Configured custom domain
|
|
360
|
+
- [ ] (Optional) Added Google Safe Browsing API key
|