152 lines
3.2 KiB
Markdown
152 lines
3.2 KiB
Markdown
# URL Shortener
|
|
|
|
A minimal URL shortener written in pure Python 3.8+ (zero third-party dependencies) backed by SQLite.
|
|
|
|
## Quick start
|
|
|
|
### Run locally
|
|
|
|
```bash
|
|
python urlshort.py config.json
|
|
```
|
|
|
|
### Run with Docker Compose
|
|
|
|
Edit `config.json` first (especially `api_key` and `base_url`), then:
|
|
|
|
```bash
|
|
docker compose up --build
|
|
```
|
|
|
|
The SQLite database is stored in `./data/urlshort.db` on the host — it survives container restarts.
|
|
|
|
---
|
|
|
|
## Configuration (`config.json`)
|
|
|
|
| Key | Required | Default | Description |
|
|
|----------------|----------|----------------------|----------------------------------------------|
|
|
| `base_url` | ✅ | — | Public base URL used in generated short URLs |
|
|
| `short_length` | ✅ | — | Character length of auto-generated codes |
|
|
| `api_key` | ✅ | — | Secret key to protect write/read operations |
|
|
| `host` | | `"0.0.0.0"` | Bind address |
|
|
| `port` | | `8080` | Bind port |
|
|
| `db_path` | | `"data/urlshort.db"` | Path to the SQLite database file |
|
|
|
|
---
|
|
|
|
## Authentication
|
|
|
|
All `/api/*` endpoints require the API key.
|
|
Pass it as a **Bearer token** in the `Authorization` header or as a query parameter:
|
|
|
|
```
|
|
Authorization: Bearer <api_key>
|
|
# or
|
|
?api_key=<api_key>
|
|
```
|
|
|
|
---
|
|
|
|
## API Reference
|
|
|
|
### `GET /`
|
|
Health check.
|
|
|
|
**Response `200`**
|
|
```json
|
|
{ "status": "ok", "service": "url-shortener" }
|
|
```
|
|
|
|
---
|
|
|
|
### `POST /api/shorten` 🔒
|
|
Create a new short URL.
|
|
|
|
**Request body**
|
|
```json
|
|
{
|
|
"url": "https://example.com/very/long/path",
|
|
"custom_code": "mycode" // optional
|
|
}
|
|
```
|
|
|
|
**Response `201`**
|
|
```json
|
|
{
|
|
"short_code": "aB3xYz",
|
|
"short_url": "http://localhost:8080/aB3xYz",
|
|
"original_url": "https://example.com/very/long/path",
|
|
"created_at": 1710000000
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### `GET /api/urls` 🔒
|
|
List all short URLs, newest first.
|
|
|
|
**Response `200`**
|
|
```json
|
|
{
|
|
"count": 2,
|
|
"urls": [
|
|
{
|
|
"short_code": "aB3xYz",
|
|
"short_url": "http://localhost:8080/aB3xYz",
|
|
"original_url": "https://example.com",
|
|
"created_at": 1710000000,
|
|
"visit_count": 5
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### `GET /api/urls/<code>` 🔒
|
|
Get metadata for a single short code.
|
|
|
|
**Response `200`** — same shape as one item from the list above.
|
|
**Response `404`** — code not found.
|
|
|
|
---
|
|
|
|
### `DELETE /api/urls/<code>` 🔒
|
|
Delete a short URL entry.
|
|
|
|
**Response `200`**
|
|
```json
|
|
{ "message": "Deleted 'aB3xYz'" }
|
|
```
|
|
|
|
---
|
|
|
|
### `GET /<code>`
|
|
Redirect to the original URL (HTTP 302).
|
|
Increments `visit_count` on each hit.
|
|
|
|
---
|
|
|
|
## Example with `curl`
|
|
|
|
```bash
|
|
# Shorten a URL
|
|
curl -X POST http://localhost:8080/api/shorten \
|
|
-H "Authorization: Bearer change-this-secret-key" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"url": "https://github.com"}'
|
|
|
|
# Follow the redirect
|
|
curl -L http://localhost:8080/aB3xYz
|
|
|
|
# List all URLs
|
|
curl http://localhost:8080/api/urls \
|
|
-H "Authorization: Bearer change-this-secret-key"
|
|
|
|
# Delete a URL
|
|
curl -X DELETE http://localhost:8080/api/urls/aB3xYz \
|
|
-H "Authorization: Bearer change-this-secret-key"
|
|
```
|
|
|