Skip to content

Commit dbf048d

Browse files
author
novnc
committed
release v0.8.8
2 parents 81ebf9d + aeab59c commit dbf048d

File tree

12 files changed

+356
-475
lines changed

12 files changed

+356
-475
lines changed

.github/instructions/copilot-instructions.md

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -121,70 +121,109 @@ type PasteStore interface {
121121
## Code Organization
122122

123123
```
124-
/
125-
├── main.go # Entry point, config, router setup
126-
├── config/
127-
│ └── config.go # Configuration struct and parsing
128-
├── storage/
129-
│ ├── interface.go # PasteStore interface
130-
│ ├── filesystem.go # Filesystem implementation (server mode)
131-
│ └── s3.go # S3 implementation (Lambda mode)
132-
├── handlers/
133-
│ ├── paste.go # Upload, view, raw endpoints
134-
│ ├── burn.go # Burn-after-read functionality
135-
│ ├── meta.go # Metadata endpoint
136-
│ └── system.go # Health endpoint
137-
├── models/
138-
│ └── paste.go # Paste struct and utilities
139-
├── static/
140-
│ ├── index.html # Web UI
141-
│ ├── style.css # Styling
142-
│ └── script.js # Frontend JS
143-
└── utils/
144-
├── slug.go # Slug generation
145-
└── mime.go # Content-type detection
124+
## nclip Copilot Instructions (2025)
125+
126+
This document describes the current architecture, coding conventions, testing and deployment practices for `nclip`. It's targeted at contributors and any automated copilot/coding agent assisting with the codebase.
127+
128+
### 1) Test and Script Cleanup (must follow)
129+
130+
All integration tests and scripts must clean up the artifacts they create. Recommended patterns:
131+
- Create test artifacts with a predictable prefix (e.g. `nclip-test-<slug>`), and delete only matching files during cleanup.
132+
- When prefixing isn't possible, delete files in `./data` only if they are recently modified (e.g. `-mmin -60`) and/or explicitly recorded in a temp file during the test run.
133+
- Always remove temporary files in `/tmp/` created by tests (use explicit names).
134+
135+
This avoids accidental deletion of unrelated data and keeps CI reproducible.
136+
137+
---
138+
139+
### 2) High-level Architecture
140+
141+
- Language: Go (1.25+ recommended)
142+
- HTTP framework: Gin
143+
- Storage abstraction: `PasteStore` interface (Filesystem and S3 implementations)
144+
- Data model: JSON metadata + raw binary content
145+
- Configuration: environment variables and CLI flags
146+
147+
### 3) API Endpoints (current)
148+
149+
- GET / — Web UI (upload form)
150+
- POST / — Upload paste (returns paste URL)
151+
- POST /burn/ — Burn-after-read paste
152+
- GET /{slug} — HTML view
153+
- GET /raw/{slug} — Raw content download (sets Content-Disposition)
154+
- GET /api/v1/meta/{slug} — Metadata JSON
155+
- GET /json/{slug} — Alias for metadata
156+
- GET /health — Health check
157+
158+
### 4) Data Types
159+
160+
Paste metadata (canonical):
161+
162+
```go
163+
type Paste struct {
164+
ID string `json:"id"`
165+
CreatedAt time.Time `json:"created_at"`
166+
ExpiresAt *time.Time `json:"expires_at,omitempty"`
167+
Size int64 `json:"size"`
168+
ContentType string `json:"content_type"`
169+
BurnAfterRead bool `json:"burn_after_read"`
170+
ReadCount int `json:"read_count"`
171+
}
146172
```
147173

148-
## Testing Strategy
174+
### 5) Storage interface
175+
176+
```go
177+
type PasteStore interface {
178+
StoreContent(id string, content []byte) error
179+
StoreMetadata(id string, meta *Paste) error
180+
GetMetadata(id string) (*Paste, error)
181+
GetContent(id string) ([]byte, error)
182+
Delete(id string) error
183+
IncrementReadCount(id string) error
184+
}
185+
```
186+
187+
Note: The concrete methods in this repository follow this pattern (filesystem uses files, S3 uses object + metadata JSON files).
188+
189+
### 6) Implementation notes
190+
191+
- Content-Type: Prefer client-provided `Content-Type` header; fall back to filename extension and then content-based detection. Keep `utils/mime.go` small and testable.
192+
- Filenames: Download endpoints should include a user-friendly extension (via `utils.ExtensionByMime`). Text content should be served inline; binaries as attachments.
193+
- Burn-after-read: Ensure atomic delete after the first successful read (store-level delete or transactional approach).
194+
- Slugs: Uppercase alphanumeric by default; configurable length.
195+
196+
### 7) Error handling and logging
197+
198+
- Return JSON errors: `{ "error": "message" }`
199+
- Use appropriate HTTP status codes
200+
- Prefer structured logs (key/value or JSON); guard verbose debug behind a debug flag or environment variable
149201

150-
- Unit tests for all storage implementations
151-
- HTTP endpoint tests using httptest
152-
- Mock PasteStore for handler testing
153-
- Integration tests with real storage backends
154-
- Test both Lambda and server deployment modes
202+
### 8) Testing
155203

204+
- Unit tests for utils, storage, and services
205+
- Handler tests using httptest and a Mock PasteStore
206+
- Integration tests (scripts/integration-test.sh) exercise the real binary and filesystem backend
207+
- CI runs: unit tests, `golangci-lint` (includes `gocyclo`), and integration tests on main/dev branches
156208

157-
## Environment Variables
209+
### 9) CI / Linting
158210

159-
All main configuration is via these environment variables (all have CLI flag equivalents):
211+
- `golangci-lint` is used (configurable via `.golangci.yml`). `gocyclo` is enabled; keep functions under complexity thresholds where practical. Refactor complex helpers into small, testable functions.
160212

161-
| Environment Variable | CLI Flag | Description |
162-
|--------------------------|------------------|---------------------------------------------|
163-
| NCLIP_PORT | --port | HTTP server port (default: 8080) |
164-
| NCLIP_URL | --url | Base URL for paste links |
165-
| NCLIP_SLUG_LENGTH | --slug-length | Length of generated paste IDs (default: 5) |
166-
| NCLIP_BUFFER_SIZE | --buffer-size | Max upload size in bytes (default: 5MB) |
167-
| NCLIP_TTL | --ttl | Default paste expiration (default: 24h) |
168-
| NCLIP_DATA_DIR | --data-dir | Local data directory (default: ./data) |
169-
| NCLIP_S3_BUCKET | --s3-bucket | S3 bucket for Lambda mode |
170-
| NCLIP_S3_PREFIX | --s3-prefix | S3 key prefix (optional) |
213+
### 10) Deployment
171214

172-
**Note:**
173-
- `GIN_MODE`, `AWS_LAMBDA_FUNCTION_NAME`, and `S3_BUCKET` are used only in deployment workflows (e.g., GitHub Actions, Lambda detection), not for app configuration.
174-
- NCLIP_MONGO_URL and --mongo-url are no longer supported.
215+
- Server mode: standard HTTP server with filesystem storage
216+
- Lambda mode: S3-backed, same codebase. Use `AWS_LAMBDA_FUNCTION_NAME` or environment-driven adapter to detect Lambda runtime. Wrap Gin with an adapter (aws-lambda-go-api-proxy or similar).
175217

218+
### 11) Security and operational notes
176219

177-
## Deployment Modes
220+
- No authentication required by design; consider adding rate-limiting or abuse protection for public deployments.
221+
- Ensure S3 permissions are scoped to required actions only.
178222

179-
1. **Server mode**: Uses filesystem, full HTTP server
180-
2. **AWS Lambda**: Uses S3, same codebase with Lambda adapter
181-
3. **Both Lambda and server mode should use the same main.go, and use the AWS_LAMBDA_FUNCTION_NAME variable to determine the mode. (Don't separate 2 main.go for 2 modes )**
223+
---
182224

183-
Both modes share identical API and behavior.
225+
If you want, I can also:
226+
- Convert integration-test cleanup to a prefix-based approach, or
227+
- Add a short contributor checklist in this file with exact commands to run locally (build, run server, run integration tests).
184228

185-
## Lambda Deployment with S3
186-
- The Lambda code should support http v2.0 payload format. (This is the default for new Lambda functions behind an API Gateway HTTP API.)
187-
- Use AWS Lambda Go SDK
188-
- Wrap Gin router with Lambda adapter
189-
- S3 bucket is specified via `NCLIP_S3_BUCKET` env var
190-
- Ensure the Lambda execution role has permissions for S3 actions: `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject`
229+
If you'd like this file split into separate CONTRIBUTING.md and ARCHITECTURE.md files I can create them as well.

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ jobs:
296296
if: failure()
297297
uses: actions/upload-artifact@v4
298298
with:
299-
name: integration-test-artifacts
299+
# include matrix.go-version and run id to avoid name collisions when multiple matrix jobs run in parallel
300+
name: integration-test-artifacts-${{ matrix.go-version }}-run${{ github.run_id }}
300301
path: |
301302
nclip
302303
scripts/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ go.work
4848
bootstrap
4949
main
5050
core.*
51+
.nfs.*
5152

.golangci.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: "2"
22

33
run:
44
timeout: 5m
5-
tests: false # Skip test files to avoid errcheck issues
5+
tests: false # Skip test files to avoid errcheck issues
66

77
linters:
88
enable:
@@ -14,3 +14,9 @@ linters:
1414
- misspell
1515
- unconvert
1616
- gocyclo
17+
18+
settings:
19+
gocyclo:
20+
# Minimal code complexity to report.
21+
# Default: 30 (but we recommend 10-20)
22+
min-complexity: 15

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 johnwmail
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.

README.md

Lines changed: 14 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,18 @@ A modern, high-performance HTTP clipboard app written in Go with Gin framework.
1212
## Table of Contents
1313

1414
- [Overview](#overview)
15-
- [Features](#-features)
16-
- [Quick Start](#-quick-start)
17-
- [API Endpoints](#-api-endpoints)
18-
- [Client Usage Examples](#-usage-examples)
19-
- [Storage Architecture](#storage-architecture)
20-
- [Configuration](#-configuration)
21-
- [Deployment](#deployment)
22-
- [Docker](#-docker-deployment)
23-
- [Kubernetes](#kubernetes)
24-
- [AWS Lambda](#-aws-lambda-deployment)
25-
- [Monitoring](#-monitoring)
26-
- [Development](#-development)
27-
- [Contributing](#-contributing)
28-
- [License](#-license)
29-
- [Links](#-links)
15+
- [Features](#✨-features)
16+
- [Quick Start](#🚀-quick-start)
17+
- [API Endpoints](#📋-api-endpoints)
18+
- [Storage Backends](#🗄️-storage-backends)
19+
- [Configuration](#⚙️-configuration)
20+
- [Deployment](#☁️-deployment)
21+
- [Docker](#🐳-docker-deployment)
22+
- [Kubernetes](#☸️-kubernetes-deployment)
23+
- [AWS Lambda](#☁️-aws-lambda-deployment)
24+
- [Monitoring](#📊-monitoring)
25+
- [Development](#🔧-development)
26+
- [Links](#🔗-links)
3027

3128
## Overview
3229

@@ -119,9 +116,7 @@ export NCLIP_TTL=24h
119116
./nclip
120117
```
121118

122-
## � Deployment
123-
124-
nclip supports multiple deployment methods: Docker, Kubernetes, and AWS Lambda. Choose the deployment that best fits your needs.
119+
## ☁️ Deployment
125120

126121
### Quick Start Options
127122

@@ -284,7 +279,7 @@ export NCLIP_PORT=3000
284279
./nclip --url https://demo.nclip.app --ttl 48h
285280
```
286281

287-
## �� Monitoring
282+
## 📊 Monitoring
288283

289284
- **Health Check**: `GET /health` - Returns 200 OK with system status
290285
- **Structured Logging**: JSON format with request tracing
@@ -332,79 +327,6 @@ go run main.go
332327
bash scripts/integration-tests.sh
333328
```
334329

335-
### Project Structure
336-
```
337-
/
338-
├── main.go # Unified entry point (server mode + Lambda)
339-
├── main_test.go # Integration tests
340-
├── config/ # Configuration management
341-
│ ├── config.go # Configuration loading from env vars and CLI flags
342-
│ └── config_test.go # Configuration tests
343-
├── handlers/ # HTTP request handlers
344-
│ ├── paste.go # Main paste upload/retrieval handler
345-
│ ├── paste_test.go # Paste handler tests
346-
│ ├── meta.go # Metadata API handler
347-
│ ├── meta_test.go # Metadata handler tests
348-
│ ├── system.go # System endpoints (health, etc.)
349-
│ ├── system_test.go # System handler tests
350-
│ ├── webui.go # Web UI handler
351-
│ ├── webui_test.go # Web UI tests
352-
│ ├── retrieval/ # Paste retrieval handlers
353-
│ └── upload/ # Paste upload handlers
354-
├── internal/ # Private application code
355-
│ └── services/ # Business logic services
356-
│ └── paste_service.go # Paste business logic
357-
├── models/ # Data models and structures
358-
│ ├── paste.go # Paste data model
359-
│ └── paste_test.go # Paste model tests
360-
├── storage/ # Storage abstraction layer
361-
│ ├── interface.go # PasteStore interface definition
362-
│ ├── interface_test.go # Interface tests
363-
│ ├── filesystem.go # Filesystem storage (server mode)
364-
│ ├── filesystem_test.go # Filesystem storage tests
365-
│ ├── s3.go # S3 storage (Lambda mode)
366-
│ ├── s3_test.go # S3 storage tests
367-
│ ├── s3util.go # S3 utility functions
368-
│ ├── s3util_test.go # S3 utility tests
369-
│ └── storage_test.go # Storage integration tests
370-
├── utils/ # Shared utilities
371-
│ ├── debug.go # Debug logging utilities
372-
│ ├── debug_test.go # Debug utility tests
373-
│ ├── mime.go # MIME type detection
374-
│ ├── mime_test.go # MIME detection tests
375-
│ ├── slug.go # Slug generation utilities
376-
│ └── slug_test.go # Slug generation tests
377-
├── static/ # Static web assets
378-
│ ├── index.html # Main web UI
379-
│ ├── favicon.ico # Favicon
380-
│ ├── style.css # CSS styles
381-
│ ├── script.js # JavaScript functionality
382-
│ └── view.html # Paste view template
383-
├── docs/ # Documentation
384-
│ ├── CLIENTS.md # Client usage examples
385-
│ ├── CONTAINER_CLEANUP.md # Container management
386-
│ ├── INTEGRATION-TESTS.md # Integration testing
387-
│ ├── KUBERNETES.md # Kubernetes deployment
388-
│ └── LAMBDA.md # AWS Lambda deployment
389-
├── k8s/ # Kubernetes manifests
390-
│ ├── deployment.yaml # Deployment configuration
391-
│ ├── service.yaml # Service configuration
392-
│ ├── ingress.yaml # Ingress configuration
393-
│ ├── namespace.yaml # Namespace definition
394-
│ ├── kustomization.yaml # Kustomize configuration
395-
│ └── pvc.yaml # Persistent volume claim
396-
├── scripts/ # Utility scripts
397-
│ └── integration-test.sh # Integration test runner
398-
├── .github/ # GitHub configuration
399-
│ └── workflows/ # GitHub Actions workflows
400-
├── Dockerfile # Docker image definition
401-
├── docker-compose.yml # Docker Compose configuration
402-
├── go.mod # Go module definition
403-
├── go.sum # Go module checksums
404-
├── .golangci.yml # Go linting configuration
405-
└── .gitignore # Git ignore rules
406-
```
407-
408330
## 🔗 Links
409331

410332
- **Documentation**: [docs/](docs/)

main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,12 @@ func main() {
9494
log.Println("Lambda mode: Using S3 storage")
9595
} else {
9696
// Server mode: Use filesystem
97-
store, err = storage.NewFilesystemStore()
97+
// Ensure data directory exists before initializing filesystem storage
98+
dataDir := os.Getenv("NCLIP_DATA_DIR")
99+
if dataDir == "" {
100+
dataDir = "./data"
101+
}
102+
store, err = storage.NewFilesystemStore(dataDir)
98103
if err != nil {
99104
log.Fatalf("Failed to initialize filesystem storage: %v", err)
100105
}

0 commit comments

Comments
 (0)