Skip to content

shinegami-2002/linkvault

Repository files navigation

LinkVault

CI Go License: MIT

A Go REST API for link and bookmark management with JWT/API-key auth, Redis-backed rate limiting, and PostgreSQL full-text search.

Architecture

                           ┌─────────────────────────────────────────────────────────┐
                           │                        LinkVault                        │
                           │                                                         │
  Client ──► Chi Router ──►│  Middleware                  Application                │
             (v5)          │  ┌──────────────────┐        ┌───────────────────────┐  │
                           │  │ Request ID       │        │ Handlers              │  │
                           │  │ Real IP          │───────►│   ├── Auth            │  │
                           │  │ Structured Log   │        │   ├── Link            │  │
                           │  │ Panic Recovery   │        │   ├── API Key         │  │
                           │  │ JWT / API Key    │        │   └── Health          │  │
                           │  │ Rate Limiter     │        │          │             │  │
                           │  │ Gzip Compression │        │          ▼             │  │
                           │  └──────────────────┘        │ Services               │  │
                           │                              │   ├── AuthService      │  │
                           │                              │   ├── LinkService      │  │
                           │                              │   └── APIKeyService    │  │
                           │                              │          │             │  │
                           │                              │          ▼             │  │
                           │                              │ Repositories           │  │
                           │                              │   ├── UserRepo         │  │
                           │                              │   ├── LinkRepo         │  │
                           │                              │   └── APIKeyRepo       │  │
                           │                              └───────────┬───────────┘  │
                           └──────────────────────────────────────────┼──────────────┘
                                                                      │
                                                         ┌────────────┴────────────┐
                                                         │                         │
                                                    PostgreSQL 16            Redis 7
                                                    (data + FTS)         (rate limits)

Features

  • Dual authentication - JWT (access + refresh tokens) and revocable API keys (X-API-Key header)
  • Full-text search - PostgreSQL-native tsvector search across link titles, descriptions, and tags
  • Rate limiting - Redis sorted-set sliding window, per-user, with X-RateLimit-* response headers
  • Pagination and sorting - Cursor-free page/limit pagination with configurable sort fields
  • Tag filtering - Filter saved links by tag
  • Structured logging - JSON logs via zerolog with request ID correlation
  • Graceful shutdown - Handles SIGINT/SIGTERM, drains in-flight requests
  • Auto-migrations - SQL migrations run on startup
  • Health endpoint - Reports status of API, database, and Redis
  • Docker Compose - One command to run the full stack (app + Postgres + Redis)
  • CI pipeline - GitHub Actions with lint, test (race detector), build, and Docker image verification

Quick Start

# Clone the repo
git clone https://github.com/shinegami-2002/linkvault.git
cd linkvault

# Start everything (app + PostgreSQL + Redis)
docker compose up --build -d

# Verify it's running
curl http://localhost:8080/health
# {"data":{"status":"ok","db":"ok","redis":"ok"}}

The API is available at http://localhost:8080. Migrations run automatically on startup.

To stop:

docker compose down -v

API Endpoints

Public

Method Path Description
GET /health Health check (API, DB, Redis)
GET /docs OpenAPI 3.0 spec (YAML)
POST /api/v1/auth/register Register a new user
POST /api/v1/auth/login Log in, receive JWT tokens
POST /api/v1/auth/refresh Refresh access token

Protected (requires JWT or API key)

Method Path Description
POST /api/v1/links Create a link
GET /api/v1/links List links (paginated, filterable by tag, sortable)
GET /api/v1/links/search?q= Full-text search across links
GET /api/v1/links/{id} Get a link by ID
PUT /api/v1/links/{id} Update a link
DELETE /api/v1/links/{id} Delete a link
POST /api/v1/keys Create an API key (JWT only)
GET /api/v1/keys List API keys (JWT only)
DELETE /api/v1/keys/{id} Revoke an API key (JWT only)

Authentication

LinkVault supports two authentication methods. Both can be used on link endpoints; API key management requires JWT.

JWT (Bearer Token)

Register or log in to receive an access token and a refresh token:

# Register
curl -X POST http://localhost:8080/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"securepassword123"}'

# Use the access token
curl http://localhost:8080/api/v1/links \
  -H "Authorization: Bearer <access_token>"

# Refresh when expired
curl -X POST http://localhost:8080/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"<refresh_token>"}'

API Key

Create an API key (requires JWT), then use it via the X-API-Key header:

# Create key
curl -X POST http://localhost:8080/api/v1/keys \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI Pipeline Key"}'
# Response includes raw_key -- save it, it is only shown once

# Use the key
curl http://localhost:8080/api/v1/links \
  -H "X-API-Key: lv_abc123def456ghi789"

Rate Limiting

Rate limiting uses a Redis sorted-set sliding window scoped per user. Default: 100 requests per 60 seconds (configurable via RATE_LIMIT and RATE_WINDOW env vars).

Every response includes rate limit headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1710000060

When the limit is exceeded, the API returns 429 Too Many Requests. If Redis is unavailable, the limiter fails open (requests are allowed).

Project Structure

linkvault/
├── cmd/
│   └── server/
│       └── main.go              # Entrypoint, wiring, graceful shutdown
├── internal/
│   ├── auth/
│   │   ├── jwt.go               # JWT token creation and validation
│   │   ├── jwt_test.go
│   │   ├── apikey.go            # API key hashing and verification
│   │   └── apikey_test.go
│   ├── config/
│   │   └── config.go            # Env-based config (caarlos0/env)
│   ├── handler/
│   │   ├── handler.go           # JSON response helpers
│   │   ├── auth.go              # Register, login, refresh
│   │   ├── link.go              # Link CRUD + search
│   │   ├── apikey.go            # API key create, list, revoke
│   │   └── health.go            # Health check
│   ├── middleware/
│   │   ├── auth.go              # JWT + API key auth middleware
│   │   ├── auth_test.go
│   │   ├── ratelimit.go         # Redis sliding-window rate limiter
│   │   ├── logging.go           # Structured request logging
│   │   └── recovery.go          # Panic recovery
│   ├── model/
│   │   ├── user.go              # User model
│   │   ├── link.go              # Link model
│   │   ├── apikey.go            # API key model
│   │   └── response.go          # Envelope, pagination, error types
│   ├── repository/
│   │   ├── user.go              # User SQL queries
│   │   ├── link.go              # Link SQL queries + full-text search
│   │   └── apikey.go            # API key SQL queries
│   └── service/
│       ├── auth.go              # Auth business logic
│       ├── link.go              # Link business logic
│       └── apikey.go            # API key business logic
├── migrations/
│   ├── 000001_init.up.sql       # Schema creation
│   └── 000001_init.down.sql     # Schema teardown
├── docs/
│   └── openapi.yaml             # OpenAPI 3.0 specification
├── .github/
│   └── workflows/
│       └── ci.yml               # Lint + test + build + Docker
├── Dockerfile                   # Multi-stage build (Alpine)
├── docker-compose.yml           # App + PostgreSQL 16 + Redis 7
├── Makefile                     # Build, test, lint, migrate, docker
├── go.mod
└── go.sum

Development

Prerequisites

  • Go 1.24+
  • PostgreSQL 16
  • Redis 7
  • golangci-lint (for linting)
  • Docker and Docker Compose (optional, for containerized setup)

Local Setup (without Docker)

# Set environment variables
export DATABASE_URL="postgres://linkvault:linkvault@localhost:5432/linkvault?sslmode=disable"
export REDIS_URL="redis://localhost:6379/0"
export JWT_SECRET="your-secret-here"
export PORT=8080
export RATE_LIMIT=100
export RATE_WINDOW=60

# Build and run
make run

Testing

make test
# Runs: go test -v -race -count=1 ./...

Linting

make lint
# Runs: golangci-lint run ./...

Makefile Targets

Target Description
make build Compile binary
make run Build and run locally
make test Run tests with race detector
make lint Run golangci-lint
make migrate-up Apply database migrations
make migrate-down Roll back database migrations
make docker-up Start full stack via Docker Compose
make docker-down Stop and remove containers + volumes

Tech Stack

Component Technology
Language Go 1.24
Router chi v5
Database PostgreSQL 16 (full-text search via tsvector)
Cache / Rate Limit Redis 7 (sorted-set sliding window)
Auth JWT (golang-jwt v5) + bcrypt API keys
Logging zerolog (structured JSON)
Config caarlos0/env (env vars)
Testing Go standard testing + testify
CI GitHub Actions (lint, test, build, Docker)
Container Multi-stage Docker build (Alpine)
API Spec OpenAPI 3.0

License

MIT

About

Go REST API for link/bookmark management with JWT/API-key auth, Redis rate limiting, and PostgreSQL full-text search

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages