Skip to content

Commit 33e7a75

Browse files
committed
Initial commit
0 parents  commit 33e7a75

10 files changed

Lines changed: 987 additions & 0 deletions

File tree

.dockerignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Git
2+
.git
3+
.gitignore
4+
5+
# CI/CD
6+
.github
7+
8+
# Documentation
9+
README.md
10+
11+
# Data files
12+
*.db
13+
data/
14+
15+
# Docker files
16+
Dockerfile
17+
docker-compose.yml
18+
.dockerignore
19+
20+
# Environment files (contains secrets)
21+
.env
22+
23+
# OS generated files
24+
.DS_Store
25+
Thumbs.db

.github/workflows/docker-image.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Docker Image CI
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
tags: [ 'v*.*.*' ]
7+
pull_request:
8+
branches: [ "main" ]
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: ${{ github.repository }}
13+
14+
15+
jobs:
16+
build:
17+
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: read
21+
packages: write
22+
id-token: write
23+
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: Install cosign
29+
if: github.event_name != 'pull_request'
30+
uses: sigstore/cosign-installer@v3.5.0
31+
with:
32+
cosign-release: 'v2.2.4'
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/setup-buildx-action@v3
36+
37+
- name: Log into registry ${{ env.REGISTRY }}
38+
if: github.event_name != 'pull_request'
39+
uses: docker/login-action@v3
40+
with:
41+
registry: ${{ env.REGISTRY }}
42+
username: ${{ github.actor }}
43+
password: ${{ secrets.GITHUB_TOKEN }}
44+
45+
- name: Extract Docker metadata
46+
id: meta
47+
uses: docker/metadata-action@v5
48+
with:
49+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
50+
tags: |
51+
type=raw,value=latest,enable={{is_default_branch}}
52+
type=sha
53+
type=ref,event=branch
54+
type=ref,event=pr
55+
56+
- name: Build and push Docker image
57+
id: build-and-push
58+
uses: docker/build-push-action@v5
59+
with:
60+
context: .
61+
push: ${{ github.event_name != 'pull_request' }}
62+
tags: ${{ steps.meta.outputs.tags }}
63+
labels: ${{ steps.meta.outputs.labels }}
64+
cache-from: type=gha
65+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# Dependency directories (uncomment if using dep)
15+
# vendor/
16+
17+
# Go workspace file
18+
go.work
19+
20+
# Binaries
21+
/bin/
22+
/dist/
23+
24+
# Database files
25+
*.db
26+
data/
27+
28+
# IDE files
29+
.vscode/
30+
.idea/
31+
32+
# OS files
33+
.DS_Store
34+
Thumbs.db
35+
36+
# Environment files
37+
.env

Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM golang:1.24-alpine AS builder
2+
3+
WORKDIR /app
4+
5+
COPY go.mod go.sum ./
6+
RUN go mod download
7+
COPY . .
8+
9+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o short-it ./cmd/short-it
10+
FROM alpine:latest
11+
12+
RUN apk --no-cache add ca-certificates
13+
14+
WORKDIR /root/
15+
16+
COPY --from=builder /app/short-it .
17+
RUN mkdir -p /data
18+
19+
EXPOSE 8080
20+
ENV DB_PATH=/data/short-it.db
21+
22+
CMD ["./short-it"]

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# short-it
2+
3+
A minimal, self-hosted URL shortener written in Go using bbolt (BoltDB). This project is inspired by [Erisa's worker-links](https://github.com/Erisa/worker-links). I wanted something that can do functionally the same as that project, without being fully locked into Cloudflare's services.
4+
5+
## Features
6+
7+
- Shorten URLs with autogenerated 6-character keys
8+
- Create custom paths
9+
- List stored URLs with cursor-based pagination
10+
- Simple token-based authorization
11+
- Single-file embedded database (bbolt)
12+
13+
## Quick Start
14+
15+
Prerequisites: Go 1.24 or Docker.
16+
17+
Run locally:
18+
19+
```bash
20+
go build -o short-it ./cmd/short-it
21+
APP_TOKEN=your-secret-token DB_PATH=short-it.db PORT=8080 ./short-it
22+
```
23+
24+
Run with Docker:
25+
26+
```bash
27+
docker build -t short-it .
28+
docker run -e APP_TOKEN=your-secret-token -p 8080:8080 -v $(pwd)/data:/data short-it
29+
```
30+
31+
Or use the provided docker-compose configuration:
32+
33+
```bash
34+
docker-compose up -d
35+
# edit docker-compose.yml to set APP_TOKEN or update the environment
36+
```
37+
38+
The server listens on `PORT` (default `8080`) and stores the BoltDB file at `DB_PATH` (default `short-it.db`).
39+
40+
## Configuration
41+
42+
Environment variables:
43+
44+
- `APP_TOKEN` (required): token used for authorization on protected endpoints
45+
- `PORT` (optional): HTTP port (default `8080`)
46+
- `DB_PATH` (optional): path to BoltDB file (default `short-it.db`)
47+
48+
## HTTP API
49+
50+
All endpoints that modify or list data require the `Authorization` header to equal the `APP_TOKEN` value.
51+
52+
- Create a short URL
53+
- `POST /`
54+
- Headers: `Authorization`, `URL` (or JSON body `{ "url": "..." }`)
55+
- Response: `{"key":"<generated-key>"}`
56+
57+
- List URLs (paginated)
58+
- `GET /`
59+
- Headers: `Authorization`, optional `Cursor`, optional `Limit` (max 100)
60+
- Response: `{ "items": [{"key":"...","value":"..."}], "next":"<cursor>" }`
61+
62+
- Redirect
63+
- `GET /{key}`
64+
- Redirects (302) to the stored URL if found
65+
66+
- Create/Update custom path
67+
- `PUT /{path}`
68+
- Headers: `Authorization`, `URL` (or JSON body)
69+
- Response: `201 Created`
70+
71+
- Delete path
72+
- `DELETE /{path}`
73+
- Headers: `Authorization`
74+
- Response: `204 No Content`
75+
76+
Examples:
77+
78+
```bash
79+
# shorten a URL (using header)
80+
curl -X POST -H "Authorization: your-secret-token" -H "URL: https://example.com" http://localhost:8080/
81+
82+
# get redirect
83+
curl -v http://localhost:8080/:key
84+
85+
# create custom path
86+
curl -X PUT -H "Authorization: your-secret-token" -H "URL: https://custom.example" http://localhost:8080/custom
87+
88+
# delete
89+
curl -X DELETE -H "Authorization: your-secret-token" http://localhost:8080/custom
90+
91+
# list
92+
curl -H "Authorization: your-secret-token" http://localhost:8080/
93+
```
94+
95+
## Development & Tests
96+
97+
Run unit tests:
98+
99+
```bash
100+
go test ./cmd/short-it
101+
```
102+
103+
Key implementation files:
104+
105+
- [cmd/short-it/main.go](cmd/short-it/main.go#L1) — HTTP handlers and core logic
106+
- [cmd/short-it/main_test.go](cmd/short-it/main_test.go#L1) — unit tests for handlers and DB ops
107+
- [Dockerfile](Dockerfile) — container build
108+
- [docker-compose.yml](docker-compose.yml) — example compose configuration
109+
110+
## Notes
111+
112+
- The app uses a BoltDB bucket named `urls` to store key → URL mappings.
113+
- The autogenerated keys are 6 characters drawn from `a-zA-Z0-9`.
114+

0 commit comments

Comments
 (0)