Import documentation: vault/CLAUDE.md
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
+283
@@ -0,0 +1,283 @@
|
|||||||
|
# CVault — Private Code Snippet Manager
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
CVault is a private alternative to GitHub Gist. Snippets are shareable via secret links or password-protected. The app now includes a local backend with username/password accounts, Redis-backed CAPTCHA/session/rate-limit state, HTTP-only sessions, server-side snippet persistence, and server-side password/expiration checks.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
- Next.js 16 (App Router, TypeScript, Tailwind CSS v4)
|
||||||
|
- shadcn/ui — component library (Radix + Tailwind)
|
||||||
|
- Framer Motion — animations (page transitions, stagger, layout, hover)
|
||||||
|
- @uiw/react-codemirror — code editor with syntax highlighting
|
||||||
|
- next-themes — dark/light mode toggle
|
||||||
|
- Lucide React — icons
|
||||||
|
- Geist + Geist Mono — local fonts (no CDN, served from /public/fonts/)
|
||||||
|
|
||||||
|
## Color Palette (No Purple)
|
||||||
|
- Primary: green-500 (#22c55e) / green-600 hover
|
||||||
|
- Accent: amber-500 (#f59e0b) — for password/highlights
|
||||||
|
- Background dark: zinc-950 / light: zinc-50
|
||||||
|
- Card dark: zinc-900 / light: white
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
| Route | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `/` | Landing — hero, feature cards, recent snippets |
|
||||||
|
| `/login` | Username/password sign-in with local CAPTCHA |
|
||||||
|
| `/register` | Local account creation with CAPTCHA |
|
||||||
|
| `/snippets` | Library — grid/list, search, filter |
|
||||||
|
| `/snippets/new` | Create — code/text toggle, CodeMirror or textarea |
|
||||||
|
| `/snippets/[id]` | Owner view — syntax highlight, copy, share, edit, delete |
|
||||||
|
| `/snippets/[id]/edit` | Edit — pre-filled form |
|
||||||
|
| `/s/[id]` | Shared — minimal chrome, password gate |
|
||||||
|
| `/raw/[id]` | Raw plain-text output — 403 for password-protected, 404 if expired |
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
| Component | File | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Header | `components/header.tsx` | Nav, theme toggle, mobile menu |
|
||||||
|
| Footer | `components/footer.tsx` | Coaxys credit + arsamsabbagh.ir link |
|
||||||
|
| Hero | `components/hero.tsx` | Gradient text, stagger cards |
|
||||||
|
| SnippetCard | `components/snippet-card.tsx` | Layout animation, grid/list |
|
||||||
|
| CodeEditor | `components/code-editor.tsx` | CodeMirror wrapper, 13+ langs |
|
||||||
|
| CodeViewer | `components/code-viewer.tsx` | Read-only + copy button |
|
||||||
|
| PrivacyToggle | `components/privacy-toggle.tsx` | Secret/Password slider |
|
||||||
|
| PasswordGate | `components/password-gate.tsx` | Password modal |
|
||||||
|
| SearchBar | `components/search-bar.tsx` | Live filter |
|
||||||
|
| ViewToggle | `components/view-toggle.tsx` | Grid/list switch |
|
||||||
|
| TagInput | `components/tag-input.tsx` | Comma-separated tags |
|
||||||
|
| LanguageSelector | `components/language-selector.tsx` | Language dropdown |
|
||||||
|
| ExpirationSelector | `components/expiration-selector.tsx` | Expiry dropdown |
|
||||||
|
| EmptyState | `components/empty-state.tsx` | Floating icon illustration |
|
||||||
|
| ThemeProvider | `components/theme-provider.tsx` | next-themes wrapper |
|
||||||
|
| PageTransition | `components/page-transition.tsx` | AnimatePresence wrapper |
|
||||||
|
|
||||||
|
## Animation Strategy
|
||||||
|
- All Framer Motion `initial` props set to `false` — content visible by default on SSR, animations play on client mount
|
||||||
|
- `whileHover` / `whileTap` micro-interactions on buttons/cards
|
||||||
|
- Stagger children on card grids
|
||||||
|
- Layout animations on grid/list toggle
|
||||||
|
- Hero gradient shimmer via CSS keyframes
|
||||||
|
|
||||||
|
## Data Layer
|
||||||
|
- `lib/types.ts` — Snippet interface, PrivacyType, Expiration, LANGUAGES list
|
||||||
|
- `lib/mock-data.ts` — legacy demo snippets only; production does not seed demo users/snippets
|
||||||
|
- `lib/server-db.ts` — local JSON datastore in `data/db.json`, expiration helpers, public snippet sanitizer
|
||||||
|
- `lib/server-redis.ts` — Redis client with in-memory fallback
|
||||||
|
- `lib/server-security.ts` — HTTP-only sessions and local CAPTCHA backed by Redis
|
||||||
|
- `lib/server-rate-limit.ts` — Redis-backed rate limiting
|
||||||
|
- `lib/server-password.ts` — password hashing and verification
|
||||||
|
- `lib/api-client.ts` — client-side API wrapper
|
||||||
|
- `app/api/**` — auth, snippet CRUD, and shared-snippet route handlers
|
||||||
|
- `lib/utils.ts` — cn() from shadcn
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
- Public URL: `https://snp.coaxys.ir`
|
||||||
|
- Production build: `npm run build`
|
||||||
|
- Process manager: `cvault.service` systemd unit
|
||||||
|
- App upstream: `next start -H 127.0.0.1 -p 3005`
|
||||||
|
- Nginx reverse proxy: HTTPS on port 443 → `127.0.0.1:3005`
|
||||||
|
- Redis: `REDIS_URL=redis://127.0.0.1:6379`
|
||||||
|
- Service env: `CVAULT_SECRET`, `CVAULT_SECURE_COOKIES=true`, `REDIS_URL`
|
||||||
|
- All runtime dependencies local (fonts in `/public/fonts/`, icons and editor bundled from npm, no CDN/remote ESM)
|
||||||
|
- CSP restricts scripts, styles, fonts, images, media, and API connections to local/self sources
|
||||||
|
|
||||||
|
## Production Commands
|
||||||
|
- Rebuild app: `npm run build`
|
||||||
|
- Restart app: `systemctl restart cvault.service`
|
||||||
|
- Check app: `systemctl status cvault.service --no-pager -l`
|
||||||
|
- Check nginx: `nginx -t && systemctl reload nginx`
|
||||||
|
- Check Redis: `redis-cli ping`
|
||||||
|
- Inspect Redis CVault keys: `redis-cli --scan --pattern 'cvault:*'`
|
||||||
|
|
||||||
|
## Deployment Guide
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Ubuntu/Debian server
|
||||||
|
- Node.js 20+ (`node --version`)
|
||||||
|
- npm 10+ (`npm --version`)
|
||||||
|
- Redis (`apt install redis-server`)
|
||||||
|
- Nginx (`apt install nginx`)
|
||||||
|
- Certbot for SSL (`apt install certbot python3-certbot-nginx`)
|
||||||
|
|
||||||
|
### 1. Transfer / Clone the Repo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Option A — git clone (once you push to a remote)
|
||||||
|
git clone <repo-url> /srv/CVault/vault
|
||||||
|
|
||||||
|
# Option B — rsync from old server
|
||||||
|
rsync -av --exclude='.next' --exclude='node_modules' --exclude='data/' \
|
||||||
|
user@old-server:/srv/CVault/vault/ /srv/CVault/vault/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/CVault/vault
|
||||||
|
npm ci # uses package-lock.json exactly — never npm install in production
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Environment Variables
|
||||||
|
|
||||||
|
Create `/etc/cvault.env` (owned by root, mode 600):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /etc/cvault.env << 'EOF'
|
||||||
|
CVAULT_SECRET=<generate: openssl rand -hex 32>
|
||||||
|
CVAULT_SECURE_COOKIES=true
|
||||||
|
REDIS_URL=redis://127.0.0.1:6379
|
||||||
|
NODE_ENV=production
|
||||||
|
EOF
|
||||||
|
chmod 600 /etc/cvault.env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Build the App
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/CVault/vault
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. systemd Service
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/cvault.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=CVault Next.js application
|
||||||
|
After=network.target redis.service
|
||||||
|
Requires=redis.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=www-data
|
||||||
|
WorkingDirectory=/srv/CVault/vault
|
||||||
|
EnvironmentFile=/etc/cvault.env
|
||||||
|
ExecStart=/usr/bin/npm start -- -H 127.0.0.1 -p 3005
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable cvault.service
|
||||||
|
systemctl start cvault.service
|
||||||
|
systemctl status cvault.service --no-pager -l
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Nginx + SSL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Obtain certificate first
|
||||||
|
certbot certonly --nginx -d snp.coaxys.ir
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `/etc/nginx/sites-available/cvault`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name snp.coaxys.ir;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name snp.coaxys.ir;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/snp.coaxys.ir/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/snp.coaxys.ir/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
client_max_body_size 2m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3005;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ln -s /etc/nginx/sites-available/cvault /etc/nginx/sites-enabled/cvault
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s https://snp.coaxys.ir/api/auth/me | jq . # should return 401
|
||||||
|
redis-cli ping # PONG
|
||||||
|
systemctl status cvault.service --no-pager -l
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. File Permissions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# App directory owned by www-data so the service can write data/db.json
|
||||||
|
chown -R www-data:www-data /srv/CVault/vault
|
||||||
|
chmod -R 755 /srv/CVault/vault
|
||||||
|
chmod 700 /srv/CVault/vault/data # restrict db access
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL Renewal (auto via systemd timer)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl status certbot.timer # should be active
|
||||||
|
# Manual renewal test:
|
||||||
|
certbot renew --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's Done
|
||||||
|
- Full frontend with all routes
|
||||||
|
- Code + Text snippet creation toggle
|
||||||
|
- CodeMirror editor with 13 language extensions
|
||||||
|
- Privacy toggle (secret link / password-protected)
|
||||||
|
- Expiration options (never, 1h, 1d, 1w)
|
||||||
|
- Tag input
|
||||||
|
- Snippet library with grid/list view + search
|
||||||
|
- View snippet with copy, share, edit, delete
|
||||||
|
- Password gate modal
|
||||||
|
- Shared snippet minimal view
|
||||||
|
- Dark/light theme (green/amber palette, zero purple)
|
||||||
|
- Footer with Coaxys + arsamsabbagh.ir link
|
||||||
|
- Responsive on mobile/tablet/desktop
|
||||||
|
- Framer Motion animations (visible-then-animate pattern)
|
||||||
|
- All fonts/resources served locally (no CDN)
|
||||||
|
- Production build + nginx proxy (HTTPS, snp.coaxys.ir)
|
||||||
|
- systemd service enabled for boot
|
||||||
|
- No demo account or demo snippets seeded in production
|
||||||
|
- `/raw/[id]` plain-text endpoint for curl/scripts
|
||||||
|
- CodeMirror theme selector — 6 themes, persisted to localStorage
|
||||||
|
- Inline share URL bar with copy button on owner view
|
||||||
|
- Live expiration countdown on cards, owner view, and shared view
|
||||||
|
- `lib/expiry-format.ts` — shared expiry formatting utility
|
||||||
|
|
||||||
|
## Backend Status
|
||||||
|
- Local username/password authentication with HTTP-only session cookie
|
||||||
|
- Fully local CAPTCHA for login and registration, stored in Redis
|
||||||
|
- Redis-backed session storage, CAPTCHA storage, and rate limits
|
||||||
|
- Server-side hashed user and snippet passwords
|
||||||
|
- Persistent server datastore in `data/db.json`
|
||||||
|
- API endpoints for snippet CRUD and shared snippet access
|
||||||
|
- Server-side password verification for shared snippets
|
||||||
|
- Server-side expiration enforcement
|
||||||
|
|
||||||
|
## What's Remaining
|
||||||
|
- PostgreSQL adapter if this needs multi-instance deployment
|
||||||
|
- Redis persistence hardening/monitoring if sessions must survive Redis restarts
|
||||||
|
- Real-time collaboration
|
||||||
|
- Comments on snippets
|
||||||
|
- Embed widgets for shared snippets
|
||||||
|
- Snippet templates
|
||||||
Reference in New Issue
Block a user