{"id":44563729,"url":"https://github.com/cgillinger/keep","last_synced_at":"2026-02-13T23:54:46.947Z","repository":{"id":333913968,"uuid":"1137458525","full_name":"cgillinger/keep","owner":"cgillinger","description":"Self-hosted Google Keep clone with real-time sync, images, color coding, and complete data control. Perfect for families who value privacy. 🔐","archived":false,"fork":false,"pushed_at":"2026-01-28T17:33:44.000Z","size":636,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"claude/explain-codebase-mkl3wm3qrnyemrjg-6Y4yy","last_synced_at":"2026-01-29T07:59:38.141Z","etag":null,"topics":["google-keep","note-taking","privacy","self-hosted"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cgillinger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-19T11:52:58.000Z","updated_at":"2026-01-28T17:37:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cgillinger/keep","commit_stats":null,"previous_names":["cgillinger/keep"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cgillinger/keep","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgillinger%2Fkeep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgillinger%2Fkeep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgillinger%2Fkeep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgillinger%2Fkeep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cgillinger","download_url":"https://codeload.github.com/cgillinger/keep/tar.gz/refs/heads/claude/explain-codebase-mkl3wm3qrnyemrjg-6Y4yy","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgillinger%2Fkeep/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29423989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T22:20:51.549Z","status":"ssl_error","status_checked_at":"2026-02-13T22:20:49.838Z","response_time":78,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["google-keep","note-taking","privacy","self-hosted"],"created_at":"2026-02-13T23:54:46.147Z","updated_at":"2026-02-13T23:54:46.929Z","avatar_url":"https://github.com/cgillinger.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Keep Clone - Private Google Keep for Families\n\n[🇸🇪 Svenska versionen](#svenska-swedish-version)\n\nA secure, self-hosted Google Keep clone with sharing features, customizable profiles, and import from Google Keep.\n\n![Version](https://img.shields.io/badge/version-1.1.0-blue)\n![License](https://img.shields.io/badge/license-MIT-green)\n![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)\n![Docker](https://img.shields.io/badge/docker-supported-2496ED?logo=docker\u0026logoColor=white)\n![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macOS%20%7C%20windows-lightgrey)\n\n\u003e **⚠️ Hobby Project - Provided \"AS IS\"**\n\u003e\n\u003e This is a personal hobby project shared freely under MIT license. No support, bug fixes, or feature requests are guaranteed. Use at your own risk. Feel free to fork and modify for your needs.\n\n## ✨ Features\n\n- 📝 **Notes:** Create, edit, and organize notes\n- ☑️ **Checklists:** Checkable task lists\n- 🎨 **Color Coding:** 12 colors to choose from\n- 📌 **Pin Notes:** Keep important notes at the top\n- 📦 **Archive:** Archive notes you don't want to see right now\n- 🔍 **Search:** Find notes quickly\n- 👥 **Share:** Share notes with family members (view or edit)\n- 👤 **Personal Profiles:** Avatar colors, background themes, and initials\n- 🌙 **Night Mode:** WCAG-compliant dark theme for comfortable reading\n- 🎨 **Background Themes:** 5 light themes + night mode\n- 📥 **Import:** Import your existing notes from Google Keep\n- 📤 **Export:** Export backup of all your notes\n- 🔄 **Real-time:** Automatically syncs across all devices\n- 🔐 **Security:** Enterprise-grade security with CSRF, rate limiting, XSS protection, etc.\n- 🔑 **Password Reset:** Email-based recovery (optional)\n- 🌍 **Multi-language:** English, Swedish, Finnish, Norwegian, Danish, German, French\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- Node.js 18 or later (recommended)\n- npm (included with Node.js)\n- Approximately 200 MB disk space (for dependencies and data)\n\n### Installation\n\n1. **Clone the repository:**\n```bash\ngit clone https://github.com/cgillinger/keep.git\ncd keep\n```\n\n2. **Install dependencies:**\n```bash\nnpm install\n```\n\n3. **Configure environment variables:**\n\nCreate a `.env` file in the project's root directory:\n\n```bash\ncp .env.example .env\n```\n\n**Edit `.env` and configure:**\n\n**REQUIRED:**\n```env\nSESSION_SECRET=your_secure_random_string_here\n```\n\nGenerate a secure secret:\n```bash\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n```\n\n**OPTIONAL - Email for password reset:**\n\nIf you want users to be able to reset forgotten passwords, configure SMTP:\n\n```env\n# Email for password reset (optional)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=your.family@gmail.com\nSMTP_PASS=your_app_password_here\nEMAIL_FROM=Keep Clone \u003cyour.family@gmail.com\u003e\n```\n\n**For Gmail:**\n1. Enable 2-factor authentication on your Google account\n2. Go to https://myaccount.google.com/apppasswords\n3. Create an app password for \"Keep Clone\"\n4. Use the app password (not your regular password) in `SMTP_PASS`\n\n**For other email services:**\n- **Outlook/Hotmail:** `smtp-mail.outlook.com`, port 587\n- **Yahoo:** `smtp.mail.yahoo.com`, port 587\n- **Custom SMTP:** Contact your email provider for settings\n\n**NOTE:** If email is not configured, the app works fully, but without password reset. Users who forget passwords must create new accounts.\n\n**Complete example `.env`:**\n```env\n# Required\nSESSION_SECRET=a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8\n\n# Optional\nPORT=3000\n\n# Email (optional, for password reset)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=family@gmail.com\nSMTP_PASS=abcd efgh ijkl mnop\nEMAIL_FROM=Keep Clone \u003cfamily@gmail.com\u003e\n```\n\n4. **Start the server:**\n```bash\nnpm start\n```\n\n5. **Open in browser:**\n```\nhttp://localhost:3000\n```\n\n### First Use\n\n1. Click \"Register\"\n2. Create an account (minimum 3 characters username, 12+ characters password)\n3. Log in\n4. Customize your profile (click on your initials):\n   - Choose avatar color\n   - Choose background theme (including night mode)\n   - Enable/disable dates on notes\n5. Start creating notes!\n\n**What is created automatically:**\n- `data/keep.db` - SQLite database (created on first start)\n- `data/sessions/` - Session database\n- `data/media/` - Imported attachments from Google Keep\n\n**Tip:** Back up the `data/` folder regularly to save your notes!\n\n## 📦 Docker (recommended for production)\n\nThe repo includes all necessary files for Docker:\n- ✅ `Dockerfile` - Container configuration\n- ✅ `docker-compose.yml` - Orchestration and volumes\n- ✅ `.dockerignore` - Excludes unnecessary files\n- ✅ `.env.example` - Environment variable template\n\n### With Docker Compose (RECOMMENDED)\n\n**Step 1: Create .env file**\n\n```bash\n# Copy example file\ncp .env.example .env\n\n# Generate secure SESSION_SECRET\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n```\n\n**Edit `.env`** and set at least `SESSION_SECRET`:\n```env\nSESSION_SECRET=your_generated_secret_here\nPORT=3000\n\n# Optional: Email for password reset\n# SMTP_HOST=smtp.gmail.com\n# SMTP_PORT=587\n# ...\n```\n\n**Step 2: Start with Docker Compose**\n\n```bash\n# Build and start (first time)\ndocker-compose up -d\n\n# View logs (real-time)\ndocker-compose logs -f\n\n# Stop (keeps data)\ndocker-compose down\n\n# Restart after code changes\ndocker-compose up -d --build\n```\n\n**Step 3: Open in browser**\n```\nhttp://localhost:3000\n```\n\n**Step 4: Register first user**\n1. Click \"Register\"\n2. Create account\n3. Start using!\n\n### What happens automatically?\n\n**Data persistence:**\n- `./data/keep.db` - Database (created automatically)\n- `./data/sessions/` - Sessions\n- `./data/media/` - Imported images\n\nAll data is stored in `./data/` on your machine and survives:\n- ✅ Container restarts (`docker-compose restart`)\n- ✅ Container updates (`docker-compose up -d`)\n- ✅ Docker Compose down/up\n- ❌ **WARNING:** `docker-compose down -v` removes volumes!\n\n**Backup:** Copy the entire `./data/` folder for backups.\n\n### Manual Docker (without docker-compose)\n\nIf you prefer to run Docker directly:\n\n```bash\n# Build image\ndocker build -t kreep .\n\n# Run container\ndocker run -d \\\n  --name kreep \\\n  -p 3000:3000 \\\n  -v $(pwd)/data:/app/data \\\n  -e SESSION_SECRET=\"your_secure_secret_here\" \\\n  -e NODE_ENV=production \\\n  --restart unless-stopped \\\n  kreep\n\n# View logs\ndocker logs -f kreep\n\n# Stop and remove\ndocker stop kreep\ndocker rm kreep\n```\n\n**Tips for manual Docker:**\n- Add `-e PORT=8080` for different port\n- Add SMTP variables for email: `-e SMTP_HOST=...`\n- Use `--env-file .env` to read from .env file\n\n### Synology NAS with Container Manager\n\n**Method 1: With docker-compose.yml (easiest)**\n\n1. **Prepare project folder:**\n   - Open File Station\n   - Create folder: `/docker/keep` (or any location)\n\n2. **Upload files:**\n   - Upload **all** files from repo to the folder\n   - Or use Git (if installed): `git clone https://github.com/cgillinger/keep.git`\n\n3. **Create .env file:**\n   - Create new file in project folder: `.env`\n   - Copy content from `.env.example`\n   - Set at least: `SESSION_SECRET=your_secure_secret`\n   - Generate secret on your computer: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`\n\n4. **Use Container Manager:**\n   - Open Container Manager in DSM\n   - Go to **Project** (not Container or Image)\n   - Click **Create**\n   - Select project folder (`/docker/keep`)\n   - Container Manager automatically finds `docker-compose.yml`\n   - Click **Next** → **Done**\n\n5. **Start:**\n   - Project starts automatically\n   - Connect via: `http://[synology-ip]:3000`\n\n**Method 2: Manual container (more advanced)**\n\nIf docker-compose doesn't work:\n1. Container Manager → Image → Add → From file\n2. Select `Dockerfile` from project folder\n3. Build image\n4. Container → Create\n5. Configure:\n   - Port: 3000:3000\n   - Volume: Map `/docker/keep/data` → `/app/data`\n   - Environment: Add `SESSION_SECRET`, `NODE_ENV=production`\n\n**Tips for Synology:**\n- ✅ Data in `./data/` automatically included in Hyper Backup\n- ✅ Use Synology Firewall for security\n- ✅ Configure reverse proxy for HTTPS (optional)\n- ✅ Schedule restart in Task Scheduler (optional)\n\n### Tailscale Access (recommended for security)\n\nFor secure remote access without exposing the server publicly:\n\n1. **Install Tailscale:**\n   - On server/NAS: Follow instructions at tailscale.com\n   - On your devices: Download Tailscale app\n\n2. **Connect via Tailscale network:**\n   ```\n   http://[tailscale-ip]:3000\n   ```\n   - Find Tailscale IP in Tailscale app\n   - No port forwarding needed\n   - Encrypted connection automatically\n\n3. **Benefits:**\n   - ✅ No internet exposure\n   - ✅ End-to-end encryption\n   - ✅ Works behind NAT/firewall\n   - ✅ Access from mobile/computer anywhere\n\n### Docker Troubleshooting\n\n**Problem: Container won't start**\n\n```bash\n# Show detailed logs\ndocker-compose logs\n\n# Or for manual Docker\ndocker logs kreep\n```\n\n**Common errors:**\n\n**\"SESSION_SECRET not configured\"**\n- Solution: Check that `.env` exists and contains SESSION_SECRET\n\n**\"Port already in use\"**\n- Solution: Change external port in docker-compose.yml:\n  ```yaml\n  ports:\n    - \"8080:3000\"  # Use 8080 instead\n  ```\n\n**\"Permission denied\" for data folder**\n- Solution (Linux):\n  ```bash\n  sudo chown -R $USER:$USER ./data\n  chmod -R 755 ./data\n  ```\n\n**Container stops after startup**\n- Check logs: `docker-compose logs`\n- Common: Database file corrupt → Delete `data/keep.db` and restart\n\n**Cannot connect to container**\n- Check that container is running: `docker-compose ps`\n- Check port: `docker-compose port kreep 3000`\n- Test locally first: `curl http://localhost:3000`\n\n**Update to new version**\n```bash\n# Stop container\ndocker-compose down\n\n# Pull latest changes\ngit pull\n\n# Rebuild and start\ndocker-compose up -d --build\n```\n\n**Complete reset (DELETES ALL DATA!)**\n```bash\ndocker-compose down -v  # -v deletes volumes!\nrm -rf data/\ndocker-compose up -d\n```\n\n## 📚 Documentation\n\n- **[FEATURES.md](./FEATURES.md)** - Complete feature and security documentation\n- **[IMPORT-GUIDE.md](./IMPORT-GUIDE.md)** - Detailed guide for Google Keep import\n- **[INSTALL-SYSTEMD.md](./INSTALL-SYSTEMD.md)** - Installation as systemd service on Linux\n- **[DEPLOYMENT.md](./DEPLOYMENT.md)** - 🔒 HTTPS/HTTP configuration guide for different environments\n- **[LOGGING.md](./LOGGING.md)** - 📊 Logging, monitoring, and troubleshooting guide\n\n### Quick Guides\n\n**Customize your profile:**\n1. Click on your initials in the header\n2. Choose avatar color (10 colors)\n3. Choose background theme:\n   - Default (white)\n   - Warm beige\n   - Soft blue\n   - Mint green\n   - Light lavender\n   - Night mode (dark, WCAG-compliant)\n4. Enable \"Show when created\" to see creation date on notes\n\n**Share a note:**\n1. Open the note\n2. Click the share icon (👥)\n3. Choose \"View\" or \"Edit\" for family member\n4. They get immediate access with real-time sync!\n\n**Import from Google Keep:**\n1. Go to [Google Takeout](https://takeout.google.com/)\n2. Select only \"Keep\" and download\n3. Click on your profile → \"📥 Import from Google Keep\"\n4. Select zip file and import\n5. See [IMPORT-GUIDE.md](./IMPORT-GUIDE.md) for more details\n\n## 📥 Import from Google Keep\n\nKeep Clone has built-in import from Google Keep! Move all your notes easily.\n\n### Quick Instructions\n\n1. **Export from Google:** Go to [Google Takeout](https://takeout.google.com/), select only \"Keep\", download zip\n2. **Import:** Open profile → \"📥 Import from Google Keep\", select zip file, click \"Import\"\n3. **Done!** All notes imported with colors, checklists, and attachments\n\n### What is imported?\n\n✅ **Imported:**\n- Notes with titles and content\n- Checklists with checked status\n- Color coding (12 Google Keep colors mapped to corresponding)\n- Archived notes\n- Timestamps (created/updated)\n- Attachments (images, files)\n\n❌ **NOT imported:**\n- Trash\n- Labels/tags\n- Reminders\n- Shares (become private notes)\n\nFor detailed guide and troubleshooting, see [IMPORT-GUIDE.md](./IMPORT-GUIDE.md)\n\n## 🔐 Security\n\nKeep Clone is built with security first, suitable for Tailscale access or private networks:\n\n- ✅ **Strong authentication:** Bcrypt hashing (12 rounds), 12+ character passwords\n- ✅ **CSRF protection:** All changes protected with tokens\n- ✅ **Rate limiting:** Prevents brute-force (5 login attempts/15 min in production)\n- ✅ **XSS protection:** DOMPurify sanitizes all user input\n- ✅ **Security headers:** Helmet with CSP, HSTS, X-Frame-Options\n- ✅ **Path traversal protection:** Safe file handling\n- ✅ **Secure sessions:** HTTP-only, SameSite strict cookies\n- ✅ **WebSocket auth:** Validated session on all WS connections\n- ✅ **SQL injection protection:** Parameterized queries\n\n**Rate limits (production):**\n- Login: 5 attempts / 15 minutes\n- Register: 3 registrations / hour\n- Import: 10 imports / hour\n- API: 100 calls / minute\n\n**Development mode has more generous limits for testing.**\n\nRead more in [FEATURES.md#security-features](./FEATURES.md#säkerhetsfunktioner)\n\n## 👥 Share Notes\n\nShare notes with family members:\n\n**Two permission levels:**\n- **View:** Can read and check checkboxes\n- **Edit:** Can make changes to the note\n\n**How to share:**\n1. Open the note\n2. Click the share icon (👥)\n3. Select family member and permission\n4. Done! Real-time synchronization activated\n\n**Features:**\n- See who has shared notes with you\n- Real-time updates when someone changes\n- Avatars show who owns/shares the note\n- Toggle between \"My notes\" and \"Shared with me\"\n\n## 🏗️ Architecture\n\n**Backend:**\n- Node.js + Express\n- SQLite for database\n- WebSocket (ws) for real-time sync\n- Session-based authentication\n\n**Frontend:**\n- Vanilla JavaScript (no framework)\n- Modular CSS architecture (6 files)\n- Responsive design\n- Real-time updates\n\n**Security:**\n- helmet - HTTP security headers\n- express-rate-limit - Rate limiting\n- csurf - CSRF protection\n- bcryptjs - Password hashing\n- dompurify + jsdom - XSS prevention\n- sharp - Safe image optimization\n\n## 📊 Database Structure\n\n```\nusers\n  ├─ id\n  ├─ username (unique)\n  ├─ password_hash\n  ├─ email (nullable)\n  ├─ avatar_color\n  ├─ background_theme\n  ├─ reset_token (nullable)\n  ├─ reset_token_expires (nullable)\n  └─ created_at\n\nnotes\n  ├─ id\n  ├─ user_id → users.id\n  ├─ title\n  ├─ content\n  ├─ color\n  ├─ is_checklist\n  ├─ checklist_items (JSON)\n  ├─ images (JSON array)\n  ├─ is_archived\n  ├─ is_pinned\n  ├─ created_at\n  └─ updated_at\n\nshares\n  ├─ id\n  ├─ note_id → notes.id (CASCADE)\n  ├─ shared_by_user_id → users.id\n  ├─ shared_with_user_id → users.id\n  ├─ permission (view/edit)\n  └─ created_at\n```\n\n## 🛠️ API Endpoints\n\n### Authentication\n- `POST /api/auth/register` - Register new user\n- `POST /api/auth/login` - Log in\n- `POST /api/auth/logout` - Log out\n- `GET /api/me` - Check session\n- `GET /api/csrf-token` - Get CSRF token\n- `POST /api/auth/request-reset` - Request password reset\n- `POST /api/auth/reset-password` - Reset password\n\n### Notes\n- `GET /api/notes?archived=true\u0026shared=true` - Get notes\n- `POST /api/notes` - Create note (CSRF)\n- `PUT /api/notes/:id` - Update note (CSRF)\n- `DELETE /api/notes/:id` - Delete note (CSRF)\n- `PUT /api/notes/:id/pin` - Pin/unpin note (CSRF)\n- `PUT /api/notes/:id/archive` - Archive/restore note (CSRF)\n\n### Sharing\n- `POST /api/notes/:id/share` - Share note (CSRF)\n- `DELETE /api/notes/:noteId/share/:userId` - Unshare (CSRF)\n- `GET /api/notes/:id/shares` - Get shares\n- `GET /api/users` - List users (for sharing)\n\n### Profile \u0026 Data\n- `POST /api/profile/avatar-color` - Change avatar color (CSRF)\n- `POST /api/profile/background-theme` - Change background theme (CSRF)\n- `POST /api/import` - Import Google Keep (CSRF)\n- `GET /api/export` - Export backup (ZIP)\n\n## 🔧 Configuration\n\n### Environment Variables\n\nAll configurations are done via the `.env` file:\n\n```env\n# Required\nSESSION_SECRET=your_secure_secret_here\n\n# Optional\nPORT=3000\nNODE_ENV=production\n\n# HTTPS Configuration (optional)\n# Set to 'true' if running behind a reverse proxy with TLS termination\n# This enables HSTS (HTTP Strict Transport Security) headers\nFORCE_HTTPS=false\n\n# Email (optional, for password reset)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=family@gmail.com\nSMTP_PASS=app_password_here\nEMAIL_FROM=Keep Clone \u003cfamily@gmail.com\u003e\n```\n\n#### FORCE_HTTPS (Advanced Configuration)\n\n**Default:** `false`\n\n**When to use:**\n- Set to `true` **ONLY** if running behind HTTPS reverse proxy with TLS termination (e.g., Nginx, Traefik, Caddy)\n- Leave as `false` for HTTP-only deployments (Docker, LAN, Tailscale without HTTPS)\n\n**What it does:**\n- When `true`: Enables HSTS (HTTP Strict Transport Security) headers\n- When `false`: Disables HTTPS-only security headers, allowing the app to work correctly over HTTP\n\n**Example scenarios:**\n\n✅ **HTTP deployment (Docker on Synology, LAN access):**\n```env\nFORCE_HTTPS=false  # or omit entirely\n```\n\n✅ **HTTPS deployment (behind Nginx reverse proxy):**\n```env\nFORCE_HTTPS=true\n```\n\n⚠️ **Common mistake:** Setting `FORCE_HTTPS=true` when accessing via HTTP will cause connection issues.\n\n### Change Port\n\n**Method 1: Via .env file (RECOMMENDED)**\n\nEdit `.env`:\n```env\nPORT=8080\n```\n\nRestart server:\n```bash\nnpm start\n```\n\nApp now runs on `http://localhost:8080`\n\n**Method 2: Via command line (temporary)**\n\n```bash\nPORT=8080 npm start\n```\n\nThis applies only to this session.\n\n**For Docker (see Docker port configuration below)**\n\n### Docker Port Configuration\n\nDocker has two ports to configure:\n- **Internal port** - port inside Docker container (where app runs)\n- **External port** - port on your computer/server (where you connect)\n\n**Format:** `external:internal`\n\n**Example 1: Run app on port 8080 outside container**\n```yaml\n# docker-compose.yml\nservices:\n  kreep:\n    ports:\n      - \"8080:3000\"  # External:Internal\n    # App runs on port 3000 inside container\n    # You connect via http://localhost:8080\n```\n\n**Example 2: Change both internal and external port**\n```yaml\nservices:\n  kreep:\n    environment:\n      - PORT=8080      # Internal port changed\n    ports:\n      - \"8080:8080\"    # Both ports 8080\n```\n\n**Example 3: Use port 80 (standard HTTP)**\n```yaml\nservices:\n  kreep:\n    ports:\n      - \"80:3000\"      # Connect via http://localhost (no port needed)\n```\n\n**Tips:**\n- Leave internal port as 3000 if possible (simpler)\n- Only change external port to avoid port conflicts\n- Port 80 requires root/admin on many systems\n\n### Data Location\n\nData is stored in `./data/`:\n- `keep.db` - SQLite database\n- `sessions/` - Session database\n- `media/` - Imported attachments from Google Keep\n\n### Rate Limiting\n\nAdjust in `server.js` (production values):\n```javascript\nconst loginLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 5 // 5 attempts\n});\n```\n\n## 🐛 Troubleshooting\n\n### Server won't start\n\n**Problem:** Port 3000 already in use\n\n**Solution:**\n\n**Option 1: Change port (recommended)**\n```bash\n# Add to .env\necho \"PORT=8080\" \u003e\u003e .env\nnpm start\n```\n\n**Option 2: Find and stop process on port 3000**\n```bash\n# Find process on port 3000\nlsof -i :3000\n# Kill process\nkill -9 \u003cPID\u003e\n```\n\n**Option 3: Temporary port change**\n```bash\nPORT=8080 npm start\n```\n\n**Problem:** \"SESSION_SECRET not configured\" or session errors\n\n**Solution:**\n- Check that you have created a `.env` file in project root\n- Make sure `SESSION_SECRET` is set to a long, random string\n- Generate a new secret: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`\n\n**Problem:** Missing modules or npm errors\n\n**Solution:**\n```bash\n# Clean and reinstall dependencies\nrm -rf node_modules package-lock.json\nnpm install\n```\n\n### Cannot log in\n\n**Problem:** Incorrect password or username\n\n**Solution:**\n- Check that username is correct (case-sensitive)\n- If you forgot password:\n  - With email configured: Use \"Forgot password?\"\n  - Without email: Create new account\n- Check that caps lock is not on\n\n### Import not working\n\n**Problem:** Wrong file format or corrupt zip\n\n**Solution:**\n- See [IMPORT-GUIDE.md](./IMPORT-GUIDE.md) for detailed troubleshooting\n- Check that file is a Google Takeout export (.zip)\n- Try unpacking locally first to verify integrity\n- Check that zip file contains a \"Keep/\" folder\n\n### WebSocket errors\n\n**Problem:** Real-time updates not working\n\n**Solution:**\n- Check that browser supports WebSocket\n- Refresh page (F5)\n- Check server console for errors\n- Some proxies block WebSocket - use direct connection or Tailscale\n\n### Email not working\n\n**Problem:** Password reset not sent\n\n**Solution:**\n- Check that SMTP settings are correct in `.env`\n- For Gmail: Use app password, not regular password\n- Test SMTP connection: `node -e \"require('./mailer.js')\"`\n- Check server console for SMTP errors\n- Some providers require approval for \"less secure apps\"\n\n### CSS/JS not loading in Docker (ERR_SSL_PROTOCOL_ERROR)\n\n**Problem:** Server starts but UI doesn't load - browser shows `ERR_SSL_PROTOCOL_ERROR` or CSS/JS files fail to load\n\n**Symptoms:**\n- Browser tries to load resources via HTTPS when server runs on HTTP\n- DevTools console shows \"Mixed Content\" errors or SSL errors\n- Page loads but is unstyled/broken\n\n**Root cause:** HTTPS-only security headers (HSTS, upgrade-insecure-requests) are enabled when running over HTTP\n\n**Solution:**\n\nCheck your `.env` file and ensure `FORCE_HTTPS` is **not** set to `true`:\n\n```env\n# For HTTP deployments (Docker, LAN, Synology)\nFORCE_HTTPS=false  # or omit this line entirely\n```\n\nThen restart the container:\n```bash\ndocker-compose down\ndocker-compose up -d\n```\n\n**When to use `FORCE_HTTPS=true`:**\n- **ONLY** when running behind HTTPS reverse proxy (Nginx, Traefik, Caddy with TLS)\n- For direct HTTP access (Docker, LAN, Tailscale without HTTPS): leave as `false`\n\nSee [Environment Variables](#environment-variables) section for more details.\n\n### Database Administration\n\n**Problem:** Need to manually manage users (delete inactive accounts, reset user data, etc.)\n\n**Solution:**\n\nKeep Clone uses SQLite, which can be managed directly using the `sqlite3` command-line tool.\n\n**Install sqlite3 (if not already installed):**\n```bash\n# Ubuntu/Debian\nsudo apt-get install sqlite3\n\n# macOS (usually pre-installed)\nbrew install sqlite3\n\n# Windows\n# Download from https://www.sqlite.org/download.html\n```\n\n**Access the database:**\n```bash\n# Navigate to your Keep Clone directory\ncd /path/to/keep\n\n# Open database\nsqlite3 data/keep.db\n```\n\n**Common administrative tasks:**\n\n**1. List all users:**\n```sql\nSELECT id, username, email, created_at FROM users;\n```\n\n**2. Delete a specific user (and all their data):**\n```sql\n-- First, check what will be deleted\nSELECT COUNT(*) FROM notes WHERE user_id = 1;  -- Replace 1 with user ID\nSELECT COUNT(*) FROM shares WHERE shared_by_user_id = 1 OR shared_with_user_id = 1;\n\n-- Delete user's notes\nDELETE FROM notes WHERE user_id = 1;\n\n-- Delete user's shares (both given and received)\nDELETE FROM shares WHERE shared_by_user_id = 1 OR shared_with_user_id = 1;\n\n-- Delete the user\nDELETE FROM users WHERE id = 1;\n```\n\n**3. Delete a user by username:**\n```sql\n-- Find the user ID first\nSELECT id, username, email FROM users WHERE username = 'johndoe';\n\n-- Then follow steps in #2 above with the correct user ID\n```\n\n**4. Reset a user's password (force them to use password reset):**\n```sql\n-- Clear password and set reset token\nUPDATE users SET password = NULL, reset_token = NULL, reset_token_expires = NULL WHERE username = 'johndoe';\n```\n\n**Note:** SQLite does not support bcrypt directly, so you cannot manually set passwords. Users must use the password reset feature or register a new account.\n\n**5. View database schema:**\n```sql\n.schema users\n.schema notes\n.schema shares\n```\n\n**6. Exit sqlite3:**\n```sql\n.quit\n```\n\n**⚠️ Important warnings:**\n- **Always backup the database before making changes:** `cp data/keep.db data/keep.db.backup`\n- **Stop the server before manual edits** to prevent database corruption\n- **SQLite has no authentication** - anyone with file access can modify the database\n- **Test queries with SELECT first** before using DELETE or UPDATE\n- **User sessions may remain active** after deletion - users will be logged out on next page refresh\n\n**Complete user deletion script:**\n```bash\n#!/bin/bash\n# delete-user.sh - Safe user deletion with backup\n\nUSER_ID=$1\n\nif [ -z \"$USER_ID\" ]; then\n  echo \"Usage: ./delete-user.sh \u003cuser_id\u003e\"\n  exit 1\nfi\n\n# Backup database\ncp data/keep.db \"data/keep.db.backup-$(date +%Y%m%d-%H%M%S)\"\n\n# Delete user and associated data\nsqlite3 data/keep.db \u003c\u003cEOF\nBEGIN TRANSACTION;\nDELETE FROM notes WHERE user_id = $USER_ID;\nDELETE FROM shares WHERE shared_by_user_id = $USER_ID OR shared_with_user_id = $USER_ID;\nDELETE FROM users WHERE id = $USER_ID;\nCOMMIT;\nSELECT 'User deleted successfully. Rows affected:';\nSELECT changes();\nEOF\n\necho \"Backup saved. Restart the server to clear sessions.\"\n```\n\nMake the script executable: `chmod +x delete-user.sh`\n\nUsage: `./delete-user.sh 5` (deletes user with ID 5)\n\n**7. Delete ALL users and start fresh:**\n\nIf you want to completely reset the application and remove all users, notes, and data, the simplest method is to delete the database file. The server will automatically create a new empty database on next start.\n\n**Method 1: Delete database (simplest)**\n```bash\n# Stop the server first (Ctrl+C if running)\n\n# Delete the database\nrm data/keep.db\n\n# Optional: Clear sessions\nrm -rf data/sessions/*\n\n# Start server - new database created automatically\nnpm start\n```\n\n**Method 2: Backup before deleting (recommended)**\n```bash\n# Backup existing database\ncp data/keep.db \"data/keep.db.backup-$(date +%Y%m%d-%H%M%S)\"\n\n# Delete database\nrm data/keep.db\n\n# Start server\nnpm start\n```\n\n**Method 3: Delete only data, keep database file**\n```bash\nsqlite3 data/keep.db \"DELETE FROM users; DELETE FROM notes; DELETE FROM shares; VACUUM;\"\n```\n\n**For Docker:**\n```bash\n# Stop container\ndocker-compose down\n\n# Delete database\nrm data/keep.db\nrm -rf data/sessions/*\n\n# Start again\ndocker-compose up -d\n```\n\n**What happens when database is deleted:**\n- ✅ `database.js` automatically creates new `keep.db` with correct schema\n- ✅ All tables (`users`, `notes`, `shares`) created from scratch\n- ✅ No users exist - you can register new ones immediately\n- ✅ No notes or shares exist - completely fresh start\n\n## 🧪 Development\n\n### Development Mode with Auto-restart\n\nFor development with automatic restart on file changes:\n\n```bash\n# Development mode\nnpm run dev\n```\n\n### Development vs Production\n\nKeep Clone has different security settings for development and production:\n\n**Development Mode (NODE_ENV != 'production'):**\n- More generous rate limits for testing\n- Login: 50 attempts/minute\n- Register: 20 attempts/minute\n- API: 500 calls/minute\n\n**Production Mode:**\n```bash\nNODE_ENV=production npm start\n```\n- Stricter security\n- Login: 5 attempts/15 min\n- Register: 3 attempts/hour\n- API: 100 calls/minute\n\n**Recommendation:** Always run in production mode on servers!\n\n### Clear Database\n\n```bash\nrm data/keep.db\n# Server creates new database on next start\n```\n\n## 📁 Project Structure\n\n```\nkeep/\n├── server.js              # Main server (1,391 lines)\n├── database.js            # Database initialization and schema\n├── import-parser.js       # Google Keep import parser\n├── export-generator.js    # Backup generator\n├── backup-parser.js       # Backup restoration\n├── mailer.js              # Email service for password reset\n├── package.json           # Dependencies and scripts\n├── .env.example           # Example environment variables\n├── docker-compose.yml     # Docker Compose configuration\n├── Dockerfile             # Docker image definition\n├── .dockerignore          # Docker build exclusions\n├── LICENSE                # MIT license\n├── public/\n│   ├── index.html         # Frontend HTML (425 lines)\n│   ├── app.js             # Frontend JavaScript (2,063 lines)\n│   └── css/               # Modular CSS architecture (1,615 lines)\n│       ├── base.css       # Variables, reset, dark mode\n│       ├── layout.css     # Header, grid\n│       ├── components.css # Buttons, cards, forms\n│       ├── modals.css     # Modal dialogs\n│       ├── utilities.css  # Utility classes\n│       └── debug.css      # Debug tools\n├── data/\n│   ├── keep.db            # SQLite database\n│   ├── sessions/          # Session database\n│   └── media/             # Imported attachments\n└── Documentation/\n    ├── README.md          # This file\n    ├── FEATURES.md        # Feature documentation (390 lines)\n    ├── IMPORT-GUIDE.md    # Import guide (293 lines)\n    └── INSTALL-SYSTEMD.md # Systemd installation\n\nTotal codebase: ~7,000 lines (excluding dependencies)\n```\n\n## 📝 Changelog\n\n### Version 1.1.0 (2026-01-23)\n\n**Improvements:**\n- 🌍 Bilingual documentation (English + Swedish)\n- 📖 English as primary language for international audiences\n- 🔗 Quick navigation between language versions\n\n### Version 1.0.0 (2025-01-23)\n\n**New features:**\n- ✨ Share notes with family members (view/edit permissions)\n- 👤 Customizable profiles with avatar colors (10 colors)\n- 🎨 Background themes (5 light + night mode)\n- 🌙 WCAG-compliant night mode with muted colors\n- 📥 Import from Google Keep via Takeout\n- 📤 Export/backup to ZIP\n- 🔄 Real-time synchronization via WebSocket\n- 📌 Pin important notes\n- 🔑 Password reset via email (optional)\n- 📅 Optional display of creation date on notes\n- 🖼️ Image support for imported notes\n\n**Security:**\n- 🔐 CSRF protection on all modification operations\n- 🚫 Rate limiting on sensitive endpoints\n- 🛡️ XSS protection with DOMPurify\n- 🔒 Secure sessions and cookies\n- 📋 Strong password requirements (12+ characters, mixed case, numbers)\n- 🏗️ Security headers with Helmet (CSP, HSTS, etc.)\n\n**Improvements:**\n- ♻️ Complete backend rewrite for security\n- 🎨 Modular CSS architecture (6 files)\n- 📱 Responsive design for mobile devices\n- ⚡ Optimized image handling with Sharp\n- 🚀 Cached rendering for faster UI\n- 📊 Complete documentation (1,500+ lines)\n\n**Architecture:**\n- 🗄️ SQLite database with auto-migration\n- 🔌 WebSocket for real-time updates\n- 📦 Session-based authentication\n- 🐳 Docker support\n\n## 📄 License\n\nMIT License - See [LICENSE](./LICENSE) for details.\n\nCopyright (c) 2025 Keep Clone Contributors\n\n## 🤝 Contributing\n\n**This is a hobby project provided \"AS IS\" under MIT license.**\n\nI'm sharing this code freely, but I don't commit to reviewing PRs, fixing bugs, or implementing features. If you find issues or want improvements, feel free to:\n\n- **Fork the project** and modify it for your needs\n- **Share your improvements** with the community (but no obligation for me to merge)\n- **Help other users** in discussions if you want to\n\nNo support or maintenance is guaranteed. This is a personal project I built for my family's use.\n\n## 💡 Planned Features\n\n- [ ] Labels/tags for organization\n- [ ] Reminders\n- [ ] Attachments on new notes (not just import)\n- [ ] Markdown support\n- [ ] Export to different formats (PDF, Markdown)\n- [ ] Mobile app (PWA)\n- [ ] Two-factor authentication\n- [ ] Backup schedule\n- [ ] Collaborative editing with cursor sync\n\n## ❓ Support\n\n**No support is provided for this hobby project.**\n\nThis code is shared \"as is\" without any guarantee of fixes or responses. However, you can:\n\n1. **Read the documentation** in this repo - it's comprehensive\n2. **Search existing issues** - someone might have solved your problem\n3. **Help each other** - community discussions are welcome\n4. **Fork and fix** - you have full access to modify the code\n\nI built this for my family and share it hoping it helps others, but I can't commit to providing support or bug fixes.\n\n## 👨‍👩‍👧‍👦 For Families\n\nKeep Clone is specially designed for families who want to:\n- 🏠 Have full control over their data\n- 🔒 Not let Google read their notes\n- 💰 Save money (completely free, open source)\n- 🤝 Easily share notes with family\n- 📱 Sync across all devices\n- 🚀 Easy setup on home server or NAS\n- 🛡️ Have enterprise security without enterprise cost\n\n**Perfect for:**\n- Shopping lists\n- Recipes\n- Todo lists\n- Family planning\n- Travel plans\n- Meeting notes\n- Ideas and brainstorming\n- Passwords and important notes\n\n---\n\n**Built with ❤️ for families who value privacy and simplicity.**\n\n**Version 1.1.0** | [Changelog](#changelog) | [License](./LICENSE) | [Documentation](#documentation)\n\n---\n\n\u003ca name=\"svenska-swedish-version\"\u003e\u003c/a\u003e\n# Svenska (Swedish Version)\n\n# Keep Clone - Privat Google Keep för familjer\n\nEn säker, självhostad Google Keep-klon med delningsfunktioner, anpassningsbara profiler och import från Google Keep.\n\n![Version](https://img.shields.io/badge/version-1.1.0-blue)\n![License](https://img.shields.io/badge/license-MIT-green)\n![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)\n![Docker](https://img.shields.io/badge/docker-supported-2496ED?logo=docker\u0026logoColor=white)\n![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macOS%20%7C%20windows-lightgrey)\n\n\u003e **⚠️ Hobbyprojekt - Tillhandahålls \"SOM DET ÄR\"**\n\u003e\n\u003e Detta är ett personligt hobbyprojekt som delas fritt under MIT-licens. Ingen support, buggfixar eller feature requests garanteras. Använd på egen risk. Du är välkommen att forka och modifiera för dina behov.\n\n## ✨ Funktioner\n\n- 📝 **Anteckningar:** Skapa, redigera och organisera anteckningar\n- ☑️ **Checklistor:** Avbockningsbara uppgiftslistor\n- 🎨 **Färgkodning:** 12 färger att välja mellan\n- 📌 **Fäst anteckningar:** Håll viktiga anteckningar högst upp\n- 📦 **Arkiv:** Arkivera anteckningar du inte vill se just nu\n- 🔍 **Sök:** Hitta anteckningar snabbt\n- 👥 **Dela:** Dela anteckningar med familjemedlemmar (visa eller redigera)\n- 👤 **Personliga profiler:** Avatarfärger, bakgrundsteman och initialer\n- 🌙 **Nattläge:** WCAG-kompatibelt mörkt tema för ögonvänlig läsning\n- 🎨 **Bakgrundsteman:** 5 ljusa teman + nattläge\n- 📥 **Import:** Importera dina befintliga anteckningar från Google Keep\n- 📤 **Export:** Exportera backup av alla dina anteckningar\n- 🔄 **Real-time:** Synkroniserar automatiskt mellan alla enheter\n- 🔐 **Säkerhet:** Företagsstandard säkerhet med CSRF, rate limiting, XSS-skydd m.m.\n- 🔑 **Lösenordsåterställning:** E-post-baserad återställning (valfritt)\n- 🌍 **Flerspråkigt:** Engelska, svenska, finska, norska, danska, tyska, franska\n\n## 🚀 Snabbstart\n\n### Förutsättningar\n\n- Node.js 18 eller senare (rekommenderat)\n- npm (medföljer Node.js)\n- ca 200 MB diskutrymme (för dependencies och data)\n\n### Installation\n\n1. **Klona repository:**\n```bash\ngit clone https://github.com/cgillinger/keep.git\ncd keep\n```\n\n2. **Installera dependencies:**\n```bash\nnpm install\n```\n\n3. **Konfigurera miljövariabler:**\n\nSkapa en `.env`-fil i projektets root-katalog:\n\n```bash\ncp .env.example .env\n```\n\n**Redigera `.env` och konfigurera:**\n\n**OBLIGATORISKT:**\n```env\nSESSION_SECRET=din_säkra_slumpmässiga_sträng_här\n```\n\nGenerera en säker secret:\n```bash\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n```\n\n**VALFRITT - E-post för lösenordsåterställning:**\n\nOm du vill att användare ska kunna återställa glömda lösenord, konfigurera SMTP:\n\n```env\n# E-post för lösenordsåterställning (valfritt)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=din.familj@gmail.com\nSMTP_PASS=ditt_applösenord_här\nEMAIL_FROM=Keep Clone \u003cdin.familj@gmail.com\u003e\n```\n\n**För Gmail:**\n1. Aktivera 2-faktorautentisering på ditt Google-konto\n2. Gå till https://myaccount.google.com/apppasswords\n3. Skapa ett applösenord för \"Keep Clone\"\n4. Använd applösenordet (inte ditt vanliga lösenord) i `SMTP_PASS`\n\n**För andra e-posttjänster:**\n- **Outlook/Hotmail:** `smtp-mail.outlook.com`, port 587\n- **Yahoo:** `smtp.mail.yahoo.com`, port 587\n- **Eget SMTP:** Kontakta din e-postleverantör för inställningar\n\n**OBS:** Om e-post inte konfigureras fungerar appen fullt ut, men utan lösenordsåterställning. Användare som glömmer lösenord måste skapa nya konton.\n\n**Komplett exempel `.env`:**\n```env\n# Obligatoriskt\nSESSION_SECRET=a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8\n\n# Valfritt\nPORT=3000\n\n# E-post (valfritt, för lösenordsåterställning)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=familj@gmail.com\nSMTP_PASS=abcd efgh ijkl mnop\nEMAIL_FROM=Keep Clone \u003cfamilj@gmail.com\u003e\n```\n\n4. **Starta servern:**\n```bash\nnpm start\n```\n\n5. **Öppna i webbläsaren:**\n```\nhttp://localhost:3000\n```\n\n### Första användningen\n\n1. Klicka på \"Registrera dig\"\n2. Skapa ett konto (minst 3 tecken användarnamn, 12+ tecken lösenord)\n3. Logga in\n4. Anpassa din profil (klicka på dina initialer):\n   - Välj avatarfärg\n   - Välj bakgrundstema (inkl. nattläge)\n   - Aktivera/avaktivera datum på anteckningar\n5. Börja skapa anteckningar!\n\n**Vad skapas automatiskt:**\n- `data/keep.db` - SQLite-databasen (skapas vid första start)\n- `data/sessions/` - Sessionsdatabas\n- `data/media/` - Importerade bilagor från Google Keep\n\n**Tips:** Backa upp `data/`-mappen regelbundet för att spara dina anteckningar!\n\n## 📦 Docker (rekommenderat för produktion)\n\nRepot innehåller alla nödvändiga filer för Docker:\n- ✅ `Dockerfile` - Container-konfiguration\n- ✅ `docker-compose.yml` - Orkestrering och volumes\n- ✅ `.dockerignore` - Exkluderar onödiga filer\n- ✅ `.env.example` - Miljövariabel-mall\n\n### Med Docker Compose (REKOMMENDERAT)\n\n**Steg 1: Skapa .env-fil**\n\n```bash\n# Kopiera exempel-filen\ncp .env.example .env\n\n# Generera säker SESSION_SECRET\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n```\n\n**Redigera `.env`** och sätt minst `SESSION_SECRET`:\n```env\nSESSION_SECRET=din_genererade_secret_här\nPORT=3000\n\n# Valfritt: E-post för lösenordsåterställning\n# SMTP_HOST=smtp.gmail.com\n# SMTP_PORT=587\n# ...\n```\n\n**Steg 2: Starta med Docker Compose**\n\n```bash\n# Bygg och starta (första gången)\ndocker-compose up -d\n\n# Se loggar (realtid)\ndocker-compose logs -f\n\n# Stoppa (behåller data)\ndocker-compose down\n\n# Starta om efter kodändring\ndocker-compose up -d --build\n```\n\n**Steg 3: Öppna i webbläsare**\n```\nhttp://localhost:3000\n```\n\n**Steg 4: Registrera första användaren**\n1. Klicka \"Registrera dig\"\n2. Skapa konto\n3. Börja använda!\n\n### Vad händer automatiskt?\n\n**Data persistence:**\n- `./data/keep.db` - Databas (skapas automatiskt)\n- `./data/sessions/` - Sessioner\n- `./data/media/` - Importerade bilder\n\nAll data lagras i `./data/` på din maskin och överlever:\n- ✅ Container-omstarter (`docker-compose restart`)\n- ✅ Container-uppdateringar (`docker-compose up -d`)\n- ✅ Docker Compose down/up\n- ❌ **VARNING:** `docker-compose down -v` tar bort volumes!\n\n**Backup:** Kopiera hela `./data/`-mappen för säkerhetskopiering.\n\n### Manuell Docker (utan docker-compose)\n\nOm du föredrar att köra Docker direkt:\n\n```bash\n# Bygg image\ndocker build -t kreep .\n\n# Kör container\ndocker run -d \\\n  --name kreep \\\n  -p 3000:3000 \\\n  -v $(pwd)/data:/app/data \\\n  -e SESSION_SECRET=\"din_säkra_secret_här\" \\\n  -e NODE_ENV=production \\\n  --restart unless-stopped \\\n  kreep\n\n# Se loggar\ndocker logs -f kreep\n\n# Stoppa och ta bort\ndocker stop kreep\ndocker rm kreep\n```\n\n**Tips för manuell Docker:**\n- Lägg till `-e PORT=8080` för annan port\n- Lägg till SMTP-variabler för e-post: `-e SMTP_HOST=...`\n- Använd `--env-file .env` för att läsa från .env-fil\n\n### Synology NAS med Container Manager\n\n**Metod 1: Med docker-compose.yml (enklast)**\n\n1. **Förbered projektmapp:**\n   - Öppna File Station\n   - Skapa mapp: `/docker/keep` (eller valfri plats)\n\n2. **Ladda upp filer:**\n   - Ladda upp **alla** filer från repot till mappen\n   - Eller använd Git (om installerat): `git clone https://github.com/cgillinger/keep.git`\n\n3. **Skapa .env-fil:**\n   - Skapa ny fil i projektmappen: `.env`\n   - Kopiera innehåll från `.env.example`\n   - Sätt minst: `SESSION_SECRET=din_säkra_secret`\n   - Generera secret på din dator: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`\n\n4. **Använd Container Manager:**\n   - Öppna Container Manager i DSM\n   - Gå till **Project** (inte Container eller Image)\n   - Klicka **Create**\n   - Välj projektmappen (`/docker/keep`)\n   - Container Manager hittar automatiskt `docker-compose.yml`\n   - Klicka **Next** → **Done**\n\n5. **Starta:**\n   - Projektet startar automatiskt\n   - Anslut via: `http://[synology-ip]:3000`\n\n**Metod 2: Manuell container (mer avancerat)**\n\nOm docker-compose inte fungerar:\n1. Container Manager → Image → Add → From file\n2. Välj `Dockerfile` från projektmappen\n3. Bygg image\n4. Container → Create\n5. Konfigurera:\n   - Port: 3000:3000\n   - Volume: Mappa `/docker/keep/data` → `/app/data`\n   - Environment: Lägg till `SESSION_SECRET`, `NODE_ENV=production`\n\n**Tips för Synology:**\n- ✅ Data i `./data/` inkluderas automatiskt i Hyper Backup\n- ✅ Använd Synology Firewall för säkerhet\n- ✅ Konfigurera omvänd proxy för HTTPS (valfritt)\n- ✅ Schemalägga omstart i Task Scheduler (valfritt)\n\n### Tailscale-åtkomst (rekommenderat för säkerhet)\n\nFör säker fjärråtkomst utan att exponera servern publikt:\n\n1. **Installera Tailscale:**\n   - På server/NAS: Följ instruktioner på tailscale.com\n   - På dina enheter: Ladda ner Tailscale-app\n\n2. **Anslut via Tailscale-nätverk:**\n   ```\n   http://[tailscale-ip]:3000\n   ```\n   - Hitta Tailscale IP i Tailscale-appen\n   - Ingen portforward behövs\n   - Krypterad anslutning automatiskt\n\n3. **Fördelar:**\n   - ✅ Ingen exponering mot internet\n   - ✅ End-to-end kryptering\n   - ✅ Fungerar bakom NAT/firewall\n   - ✅ Åtkomst från mobil/dator överallt\n\n### Docker Felsökning\n\n**Problem: Container startar inte**\n\n```bash\n# Visa detaljerade loggar\ndocker-compose logs\n\n# Eller för manuell Docker\ndocker logs kreep\n```\n\n**Vanliga fel:**\n\n**\"SESSION_SECRET not configured\"**\n- Lösning: Kontrollera att `.env` finns och innehåller SESSION_SECRET\n\n**\"Port already in use\"**\n- Lösning: Ändra extern port i docker-compose.yml:\n  ```yaml\n  ports:\n    - \"8080:3000\"  # Använd 8080 istället\n  ```\n\n**\"Permission denied\" för data-mapp**\n- Lösning (Linux):\n  ```bash\n  sudo chown -R $USER:$USER ./data\n  chmod -R 755 ./data\n  ```\n\n**Container stannar efter start**\n- Kontrollera loggar: `docker-compose logs`\n- Vanligt: Databasfil korrupt → Ta bort `data/keep.db` och starta om\n\n**Kan inte ansluta till container**\n- Kontrollera att containern körs: `docker-compose ps`\n- Kontrollera port: `docker-compose port kreep 3000`\n- Testa lokalt först: `curl http://localhost:3000`\n\n**Uppdatera till ny version**\n```bash\n# Stoppa container\ndocker-compose down\n\n# Hämta senaste ändringar\ngit pull\n\n# Bygg om och starta\ndocker-compose up -d --build\n```\n\n**Återställ helt (RADERAR ALL DATA!)**\n```bash\ndocker-compose down -v  # -v raderar volumes!\nrm -rf data/\ndocker-compose up -d\n```\n\n## 📚 Dokumentation\n\n- **[FEATURES.md](./FEATURES.md)** - Komplett funktions- och säkerhetsdokumentation\n- **[IMPORT-GUIDE.md](./IMPORT-GUIDE.md)** - Detaljerad guide för Google Keep-import\n- **[INSTALL-SYSTEMD.md](./INSTALL-SYSTEMD.md)** - Installation som systemd-tjänst på Linux\n\n### Snabbguider\n\n**Anpassa din profil:**\n1. Klicka på dina initialer i headern\n2. Välj avatarfärg (10 färger)\n3. Välj bakgrundstema:\n   - Standard (vit)\n   - Varm beige\n   - Mjuk blå\n   - Mint grön\n   - Ljus lavendel\n   - Nattläge (mörkt, WCAG-kompatibelt)\n4. Aktivera \"Visa när skapad\" för att se skapdatum på anteckningar\n\n**Dela en anteckning:**\n1. Öppna anteckningen\n2. Klicka på dela-ikonen (👥)\n3. Välj \"Visa\" eller \"Redigera\" för familjemedlem\n4. De får omedelbart åtkomst med real-time synk!\n\n**Importera från Google Keep:**\n1. Gå till [Google Takeout](https://takeout.google.com/)\n2. Välj endast \"Keep\" och ladda ner\n3. Klicka på din profil → \"📥 Importera från Google Keep\"\n4. Välj zip-filen och importera\n5. Se [IMPORT-GUIDE.md](./IMPORT-GUIDE.md) för mer detaljer\n\n## 📥 Import från Google Keep\n\nKeep Clone har inbyggd import från Google Keep! Flytta över alla dina anteckningar enkelt.\n\n### Snabbinstruktioner\n\n1. **Exportera från Google:** Gå till [Google Takeout](https://takeout.google.com/), välj endast \"Keep\", ladda ner zip\n2. **Importera:** Öppna profil → \"📥 Importera från Google Keep\", välj zip-filen, klicka \"Importera\"\n3. **Klar!** Alla anteckningar importeras med färger, checklistor och bilagor\n\n### Vad importeras?\n\n✅ **Importeras:**\n- Anteckningar med titlar och innehåll\n- Checklistor med avbockningsstatus\n- Färgkodning (12 Google Keep-färger mappas till motsvarande)\n- Arkiverade anteckningar\n- Tidsstämplar (skapad/uppdaterad)\n- Bilagor (bilder, filer)\n\n❌ **Importeras INTE:**\n- Papperskorgen (trash)\n- Etiketter/labels\n- Påminnelser\n- Delningar (blir privata anteckningar)\n\nFör detaljerad guide och felsökning, se [IMPORT-GUIDE.md](./IMPORT-GUIDE.md)\n\n## 🔐 Säkerhet\n\nKeep Clone är byggd med säkerhet i första hand, lämplig för Tailscale-åtkomst eller privata nätverk:\n\n- ✅ **Stark autentisering:** Bcrypt-hashning (12 rounds), 12+ tecken lösenord\n- ✅ **CSRF-skydd:** Alla ändringar skyddade med tokens\n- ✅ **Rate limiting:** Förhindrar brute-force (5 login-försök/15 min i produktion)\n- ✅ **XSS-skydd:** DOMPurify sanerar all user input\n- ✅ **Säkerhetsheaders:** Helmet med CSP, HSTS, X-Frame-Options\n- ✅ **Path traversal-skydd:** Säker filhantering\n- ✅ **Säkra sessioner:** HTTP-only, SameSite strict cookies\n- ✅ **WebSocket auth:** Validerad session på alla WS-anslutningar\n- ✅ **SQL injection-skydd:** Parametriserade queries\n\n**Rate limits (produktion):**\n- Login: 5 försök / 15 minuter\n- Register: 3 registreringar / timme\n- Import: 10 importer / timme\n- API: 100 anrop / minut\n\n**Utvecklingsläge har generösare limits för testning.**\n\nLäs mer i [FEATURES.md#säkerhetsfunktioner](./FEATURES.md#säkerhetsfunktioner)\n\n## 👥 Dela anteckningar\n\nDela anteckningar med familjemedlemmar:\n\n**Två behörighetsnivåer:**\n- **Visa:** Kan läsa och markera checklistor\n- **Redigera:** Kan göra ändringar i anteckningen\n\n**Så här delar du:**\n1. Öppna anteckningen\n2. Klicka på dela-ikonen (👥)\n3. Välj familjemedlem och behörighet\n4. Klart! Real-time synkronisering aktiveras\n\n**Funktioner:**\n- Se vem som delat anteckningar med dig\n- Real-time uppdateringar när någon ändrar\n- Avatarer visar vem som äger/delar anteckningen\n- Toggle mellan \"Mina anteckningar\" och \"Delade med mig\"\n\n## 🏗️ Arkitektur\n\n**Backend:**\n- Node.js + Express\n- SQLite för databas\n- WebSocket (ws) för real-time synk\n- Session-based autentisering\n\n**Frontend:**\n- Vanilla JavaScript (inget ramverk)\n- Modulär CSS-arkitektur (6 filer)\n- Responsiv design\n- Real-time uppdateringar\n\n**Säkerhet:**\n- helmet - HTTP security headers\n- express-rate-limit - Rate limiting\n- csurf - CSRF protection\n- bcryptjs - Lösenordshashning\n- dompurify + jsdom - XSS prevention\n- sharp - Säker bildoptimering\n\n## 📊 Databasstruktur\n\n```\nusers\n  ├─ id\n  ├─ username (unique)\n  ├─ password_hash\n  ├─ email (nullable)\n  ├─ avatar_color\n  ├─ background_theme\n  ├─ reset_token (nullable)\n  ├─ reset_token_expires (nullable)\n  └─ created_at\n\nnotes\n  ├─ id\n  ├─ user_id → users.id\n  ├─ title\n  ├─ content\n  ├─ color\n  ├─ is_checklist\n  ├─ checklist_items (JSON)\n  ├─ images (JSON array)\n  ├─ is_archived\n  ├─ is_pinned\n  ├─ created_at\n  └─ updated_at\n\nshares\n  ├─ id\n  ├─ note_id → notes.id (CASCADE)\n  ├─ shared_by_user_id → users.id\n  ├─ shared_with_user_id → users.id\n  ├─ permission (view/edit)\n  └─ created_at\n```\n\n## 🛠️ API Endpoints\n\n### Autentisering\n- `POST /api/auth/register` - Registrera ny användare\n- `POST /api/auth/login` - Logga in\n- `POST /api/auth/logout` - Logga ut\n- `GET /api/me` - Kontrollera session\n- `GET /api/csrf-token` - Hämta CSRF token\n- `POST /api/auth/request-reset` - Begär lösenordsåterställning\n- `POST /api/auth/reset-password` - Återställ lösenord\n\n### Anteckningar\n- `GET /api/notes?archived=true\u0026shared=true` - Hämta anteckningar\n- `POST /api/notes` - Skapa anteckning (CSRF)\n- `PUT /api/notes/:id` - Uppdatera anteckning (CSRF)\n- `DELETE /api/notes/:id` - Ta bort anteckning (CSRF)\n- `PUT /api/notes/:id/pin` - Fäst/avfästa anteckning (CSRF)\n- `PUT /api/notes/:id/archive` - Arkivera/återställ anteckning (CSRF)\n\n### Delning\n- `POST /api/notes/:id/share` - Dela anteckning (CSRF)\n- `DELETE /api/notes/:noteId/share/:userId` - Sluta dela (CSRF)\n- `GET /api/notes/:id/shares` - Hämta delningar\n- `GET /api/users` - Lista användare (för delning)\n\n### Profil \u0026 Data\n- `POST /api/profile/avatar-color` - Ändra avatarfärg (CSRF)\n- `POST /api/profile/background-theme` - Ändra bakgrundstema (CSRF)\n- `POST /api/import` - Importera Google Keep (CSRF)\n- `GET /api/export` - Exportera backup (ZIP)\n\n## 🔧 Konfiguration\n\n### Miljövariabler\n\nAlla konfigurationer görs via `.env`-filen:\n\n```env\n# Obligatoriskt\nSESSION_SECRET=din_säkra_secret_här\n\n# Valfritt\nPORT=3000\nNODE_ENV=production\n\n# HTTPS-konfiguration (valfritt)\n# Sätt till 'true' om du kör bakom en reverse proxy med TLS-terminering\n# Detta aktiverar HSTS (HTTP Strict Transport Security) headers\nFORCE_HTTPS=false\n\n# E-post (valfritt, för lösenordsåterställning)\nSMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=familj@gmail.com\nSMTP_PASS=applösenord_här\nEMAIL_FROM=Keep Clone \u003cfamilj@gmail.com\u003e\n```\n\n#### FORCE_HTTPS (Avancerad konfiguration)\n\n**Standard:** `false`\n\n**När ska den användas:**\n- Sätt till `true` **ENDAST** om du kör bakom HTTPS reverse proxy med TLS-terminering (t.ex. Nginx, Traefik, Caddy)\n- Lämna som `false` för HTTP-deployment (Docker, LAN, Tailscale utan HTTPS)\n\n**Vad den gör:**\n- När `true`: Aktiverar HSTS (HTTP Strict Transport Security) headers\n- När `false`: Inaktiverar HTTPS-specifika säkerhetsheaders, tillåter att appen fungerar korrekt över HTTP\n\n**Exempelscenarier:**\n\n✅ **HTTP-deployment (Docker på Synology, LAN-åtkomst):**\n```env\nFORCE_HTTPS=false  # eller utelämna helt\n```\n\n✅ **HTTPS-deployment (bakom Nginx reverse proxy):**\n```env\nFORCE_HTTPS=true\n```\n\n⚠️ **Vanligt misstag:** Att sätta `FORCE_HTTPS=true` när du använder HTTP kommer orsaka anslutningsproblem.\n\n### Ändra port\n\n**Metod 1: Via .env-fil (REKOMMENDERAT)**\n\nRedigera `.env`:\n```env\nPORT=8080\n```\n\nStarta om servern:\n```bash\nnpm start\n```\n\nAppen körs nu på `http://localhost:8080`\n\n**Metod 2: Via kommandoraden (tillfälligt)**\n\n```bash\nPORT=8080 npm start\n```\n\nDetta gäller endast för denna session.\n\n**För Docker (se Docker-portkonfiguration nedan)**\n\n### Docker-portkonfiguration\n\nDocker har två portar att konfigurera:\n- **Intern port** - porten inuti Docker-containern (där appen körs)\n- **Extern port** - porten på din dator/server (där du ansluter)\n\n**Format:** `extern:intern`\n\n**Exempel 1: Kör appen på port 8080 utanför containern**\n```yaml\n# docker-compose.yml\nservices:\n  kreep:\n    ports:\n      - \"8080:3000\"  # Extern:Intern\n    # Appen körs på port 3000 inuti containern\n    # Du ansluter via http://localhost:8080\n```\n\n**Exempel 2: Ändra både intern och extern port**\n```yaml\nservices:\n  kreep:\n    environment:\n      - PORT=8080      # Intern port ändras\n    ports:\n      - \"8080:8080\"    # Båda portarna 8080\n```\n\n**Exempel 3: Använd port 80 (standard HTTP)**\n```yaml\nservices:\n  kreep:\n    ports:\n      - \"80:3000\"      # Anslut via http://localhost (ingen port behövs)\n```\n\n**Tips:**\n- Lämna intern port som 3000 om möjligt (enklare)\n- Ändra endast extern port för att undvika portkonflikter\n- Port 80 kräver root/admin på många system\n\n### Datalokalisering\n\nData lagras i `./data/`:\n- `keep.db` - SQLite databas\n- `sessions/` - Sessionsdatabas\n- `media/` - Importerade bilagor från Google Keep\n\n### Rate Limiting\n\nJustera i `server.js` (produktionsvärden):\n```javascript\nconst loginLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000, // 15 minuter\n  max: 5 // 5 försök\n});\n```\n\n## 🐛 Felsökning\n\n### Servern startar inte\n\n**Problem:** Port 3000 redan används\n\n**Lösning:**\n\n**Alternativ 1: Ändra port (rekommenderat)**\n```bash\n# Lägg till i .env\necho \"PORT=8080\" \u003e\u003e .env\nnpm start\n```\n\n**Alternativ 2: Hitta och stoppa processen på port 3000**\n```bash\n# Hitta process på port 3000\nlsof -i :3000\n# Döda processen\nkill -9 \u003cPID\u003e\n```\n\n**Alternativ 3: Tillfällig portändring**\n```bash\nPORT=8080 npm start\n```\n\n**Problem:** \"SESSION_SECRET not configured\" eller sessionsfel\n\n**Lösning:**\n- Kontrollera att du har skapat en `.env`-fil i projektets root\n- Se till att `SESSION_SECRET` är satt till en lång, slumpmässig sträng\n- Generera en ny secret: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`\n\n**Problem:** Moduler saknas eller npm-fel\n\n**Lösning:**\n```bash\n# Rensa och installera om dependencies\nrm -rf node_modules package-lock.json\nnpm install\n```\n\n### Kan inte logga in\n\n**Problem:** Felaktigt lösenord eller användarnamn\n\n**Lösning:**\n- Kontrollera att användarnamnet är korrekt (case-sensitive)\n- Om du glömt lösenordet:\n  - Med e-post konfigurerad: Använd \"Glömt lösenord?\"\n  - Utan e-post: Skapa nytt konto\n- Kontrollera att caps lock inte är på\n\n### Import fungerar inte\n\n**Problem:** Fel filformat eller korrupt zip\n\n**Lösning:**\n- Se [IMPORT-GUIDE.md](./IMPORT-GUIDE.md) för detaljerad felsökning\n- Kontrollera att filen är en Google Takeout export (.zip)\n- Försök packa upp lokalt först för att verifiera integritet\n- Kontrollera att zip-filen innehåller en \"Keep/\"-mapp\n\n### WebSocket-fel\n\n**Problem:** Real-time uppdateringar fungerar inte\n\n**Lösning:**\n- Kontrollera att webbläsaren stödjer WebSocket\n- Uppdatera sidan (F5)\n- Kontrollera serverkonsolen för fel\n- Vissa proxies blockerar WebSocket - använd direkt anslutning eller Tailscale\n\n### E-post fungerar inte\n\n**Problem:** Lösenordsåterställning skickas inte\n\n**Lösning:**\n- Kontrollera att SMTP-inställningar är korrekta i `.env`\n- För Gmail: Använd applösenord, inte vanligt lösenord\n- Testa SMTP-anslutning: `node -e \"require('./mailer.js')\"`\n- Kontrollera serverkonsolen för SMTP-fel\n- Vissa providers kräver att du godkänner \"mindre säkra appar\"\n\n### CSS/JS laddas inte i Docker (ERR_SSL_PROTOCOL_ERROR)\n\n**Problem:** Servern startar men UI:t laddas inte - webbläsaren visar `ERR_SSL_PROTOCOL_ERROR` eller CSS/JS-filer laddas inte\n\n**Symtom:**\n- Webbläsaren försöker ladda resurser via HTTPS när servern körs på HTTP\n- DevTools-konsolen visar \"Mixed Content\"-fel eller SSL-fel\n- Sidan laddas men är ostyled/trasig\n\n**Grundorsak:** HTTPS-specifika säkerhetsheaders (HSTS, upgrade-insecure-requests) är aktiverade när appen körs över HTTP\n\n**Lösning:**\n\nKontrollera din `.env`-fil och se till att `FORCE_HTTPS` **inte** är satt till `true`:\n\n```env\n# För HTTP-deployment (Docker, LAN, Synology)\nFORCE_HTTPS=false  # eller utelämna denna rad helt\n```\n\nStarta sedan om containern:\n```bash\ndocker-compose down\ndocker-compose up -d\n```\n\n**När ska `FORCE_HTTPS=true` användas:**\n- **ENDAST** när du kör bakom HTTPS reverse proxy (Nginx, Traefik, Caddy med TLS)\n- För direkt HTTP-åtkomst (Docker, LAN, Tailscale utan HTTPS): lämna som `false`\n\nSe [Miljövariabler](#miljövariabler) för mer information.\n\n### Databasadministration\n\n**Problem:** Behöver manuellt hantera användare (radera inaktiva konton, återställa användardata, etc.)\n\n**Lösning:**\n\nKeep Clone använder SQLite, som kan hanteras direkt med `sqlite3` kommandoradsverktyget.\n\n**Installera sqlite3 (om det inte redan är installerat):**\n```bash\n# Ubuntu/Debian\nsudo apt-get install sqlite3\n\n# macOS (vanligtvis förinstallerat)\nbrew install sqlite3\n\n# Windows\n# Ladda ner från https://www.sqlite.org/download.html\n```\n\n**Öppna databasen:**\n```bash\n# Navigera till din Keep Clone-katalog\ncd /sökväg/till/keep\n\n# Öppna databasen\nsqlite3 data/keep.db\n```\n\n**Vanliga administrativa uppgifter:**\n\n**1. Lista alla användare:**\n```sql\nSELECT id, username, email, created_at FROM users;\n```\n\n**2. Radera en specifik användare (och all deras data):**\n```sql\n-- Först, kontrollera vad som kommer raderas\nSELECT COUNT(*) FROM notes WHERE user_id = 1;  -- Ersätt 1 med användar-ID\nSELECT COUNT(*) FROM shares WHERE shared_by_user_id = 1 OR shared_with_user_id = 1;\n\n-- Radera användarens anteckningar\nDELETE FROM notes WHERE user_id = 1;\n\n-- Radera användarens delningar (både givna och mottagna)\nDELETE FROM shares WHERE shared_by_user_id = 1 OR shared_with_user_id = 1;\n\n-- Radera användaren\nDELETE FROM users WHERE id = 1;\n```\n\n**3. Radera en användare via användarnamn:**\n```sql\n-- Hitta användar-ID först\nSELECT id, username, email FROM users WHERE username = 'johndoe';\n\n-- Följ sedan stegen i #2 ovan med korrekt användar-ID\n```\n\n**4. Återställ en användares lösenord (tvinga dem att använda lösenordsåterställning):**\n```sql\n-- Rensa lösenord och nollställ reset token\nUPDATE users SET password = NULL, reset_token = NULL, reset_token_expires = NULL WHERE username = 'johndoe';\n```\n\n**Obs:** SQLite stödjer inte bcrypt direkt, så du kan inte manuellt sätta lösenord. Användare måste använda lösenordsåterställningsfunktionen eller registrera ett nytt konto.\n\n**5. Visa databasschema:**\n```sql\n.schema users\n.schema notes\n.schema shares\n```\n\n**6. Avsluta sqlite3:**\n```sql\n.quit\n```\n\n**⚠️ Viktiga varningar:**\n- **Säkerhetskopiera alltid databasen innan ändringar:** `cp data/keep.db data/keep.db.backup`\n- **Stoppa servern innan manuella ändringar** för att undvika databaskorruption\n- **SQLite har ingen autentisering** - vem som helst med filåtkomst kan ändra databasen\n- **Testa frågor med SELECT först** innan du använder DELETE eller UPDATE\n- **Användarsessioner kan förbli aktiva** efter radering - användare loggas ut vid nästa siduppdatering\n\n**Komplett skript för användarradering:**\n```bash\n#!/bin/bash\n# delete-user.sh - Säker användarradering med backup\n\nUSER_ID=$1\n\nif [ -z \"$USER_ID\" ]; then\n  echo \"Användning: ./delete-user.sh \u003canvändar_id\u003e\"\n  exit 1\nfi\n\n# Säkerhetskopiera databas\ncp data/keep.db \"data/keep.db.backup-$(date +%Y%m%d-%H%M%S)\"\n\n# Radera användare och associerad data\nsqlite3 data/keep.db \u003c\u003cEOF\nBEGIN TRANSACTION;\nDELETE FROM notes WHERE user_id = $USER_ID;\nDELETE FROM shares WHERE shared_by_user_id = $USER_ID OR shared_with_user_id = $USER_ID;\nDELETE FROM users WHERE id = $USER_ID;\nCOMMIT;\nSELECT 'Användare raderad. Påverkade rader:';\nSELECT changes();\nEOF\n\necho \"Backup sparad. Starta om servern för att rensa sessioner.\"\n```\n\nGör skriptet körbart: `chmod +x delete-user.sh`\n\nAnvändning: `./delete-user.sh 5` (raderar användare med ID 5)\n\n**7. Radera ALLA användare och börja om från början:**\n\nOm du vill helt återställa applikationen och ta bort alla användare, anteckningar och data är den enklaste metoden att radera databasfilen. Servern kommer automatiskt skapa en ny tom databas vid nästa start.\n\n**Metod 1: Radera databasen (enklast)**\n```bash\n# Stoppa servern först (Ctrl+C om den körs)\n\n# Radera databasen\nrm data/keep.db\n\n# Valfritt: Rensa sessioner\nrm -rf data/sessions/*\n\n# Starta servern - ny databas skapas automatiskt\nnpm start\n```\n\n**Metod 2: Säkerhetskopiera innan radering (rekommenderat)**\n```bash\n# Säkerhetskopiera befintlig databas\ncp data/keep.db \"data/keep.db.backup-$(date +%Y%m%d-%H%M%S)\"\n\n# Radera databasen\nrm data/keep.db\n\n# Starta servern\nnpm start\n```\n\n**Metod 3: Radera bara innehållet, behåll databasfilen**\n```bash\nsqlite3 data/keep.db \"DELETE FROM users; DELETE FROM notes; DELETE FROM shares; VACUUM;\"\n```\n\n**För Docker:**\n```bash\n# Stoppa containern\ndocker-compose down\n\n# Radera databasen\nrm data/keep.db\nrm -rf data/sessions/*\n\n# Starta igen\ndocker-compose up -d\n```\n\n**Vad händer när databasen raderas:**\n- ✅ `database.js` skapar automatiskt ny `keep.db` med korrekt schema\n- ✅ Alla tabeller (`users`, `notes`, `shares`) skapas från början\n- ✅ Inga användare finns - du kan registrera nya direkt\n- ✅ Inga anteckningar eller delningar finns - helt ny start\n\n## 🧪 Utveckling\n\n### Utvecklingsläge med auto-restart\n\nFör utveckling med automatisk omstart vid filändringar:\n\n```bash\n# Utvecklingsläge\nnpm run dev\n```\n\n### Utveckling vs Produktion\n\nKeep Clone har olika säkerhetsinställningar för utveckling och produktion:\n\n**Utvecklingsläge (NODE_ENV != 'production'):**\n- Mer generösa rate limits för testning\n- Login: 50 försök/minut\n- Register: 20 försök/minut\n- API: 500 anrop/minut\n\n**Produktionsläge:**\n```bash\nNODE_ENV=production npm start\n```\n- Striktare säkerhet\n- Login: 5 försök/15 min\n- Register: 3 försök/timme\n- API: 100 anrop/minut\n\n**Rekommendation:** Kör alltid i produktionsläge på servrar!\n\n### Rensa databasen\n\n```bash\nrm data/keep.db\n# Servern skapar ny databas vid nästa start\n```\n\n## 📁 Projektstruktur\n\n```\nkeep/\n├── server.js              # Huvudserver (1,391 rader)\n├── database.js            # Databas-initialisering och schema\n├── import-parser.js       # Google Keep import-parser\n├── export-generator.js    # Backup-generator\n├── backup-parser.js       # Backup-återställning\n├── mailer.js              # E-posttjänst för lösenordsåterställning\n├── package.json           # Dependencies och scripts\n├── .env.example           # Exempel på miljövariabler\n├── docker-compose.yml     # Docker Compose-konfiguration\n├── Dockerfile             # Docker image-definition\n├── .dockerignore          # Docker build-exkluderingar\n├── LICENSE                # MIT-licens\n├── public/\n│   ├── index.html         # Frontend HTML (425 rader)\n│   ├── app.js             # Frontend JavaScript (2,063 rader)\n│   └── css/               # Modulär CSS-arkitektur (1,615 rader)\n│       ├── base.css       # Variabler, reset, dark mode\n│       ├── layout.css     # Header, grid\n│       ├── components.css # Knappar, kort, formulär\n│       ├── modals.css     # Modala dialoger\n│       ├── utilities.css  # Hjälpklasser\n│       └── debug.css      # Debug-verktyg\n├── data/\n│   ├── keep.db            # SQLite databas\n│   ├── sessions/          # Sessionsdatabas\n│   └── media/             # Importerade bilagor\n└── Documentation/\n    ├── README.md          # Denna fil\n    ├── FEATURES.md        # Funktionsdokumentation (390 rader)\n    ├── IMPORT-GUIDE.md    # Importguide (293 rader)\n    └── INSTALL-SYSTEMD.md # Systemd-installation\n\nTotal kodbas: ~7,000 rader (utan dependencies)\n```\n\n## 📝 Changelog\n\n### Version 1.1.0 (2026-01-23)\n\n**Förbättringar:**\n- 🌍 Tvåspråkig dokumentation (English + Swedish)\n- 📖 Engelska som huvudspråk för internationell publik\n- 🔗 Snabb navigation mellan språkversioner\n\n### Version 1.0.0 (2025-01-23)\n\n**Nya funktioner:**\n- ✨ Dela anteckningar med familjemedlemmar (view/edit permissions)\n- 👤 Anpassningsbara profiler med avatarfärger (10 färger)\n- 🎨 Bakgrundsteman (5 ljusa + nattläge)\n- 🌙 WCAG-kompatibelt nattläge med dämpade färger\n- 📥 Import från Google Keep via Takeout\n- 📤 Export/backup till ZIP\n- 🔄 Real-time synkronisering via WebSocket\n- 📌 Fäst viktiga anteckningar\n- 🔑 Lösenordsåterställning via e-post (valfritt)\n- 📅 Valfri visning av skapdatum på anteckningar\n- 🖼️ Bildstöd för importerade anteckningar\n\n**Säkerhet:**\n- 🔐 CSRF-skydd på alla ändringsoperationer\n- 🚫 Rate limiting på känsliga endpoints\n- 🛡️ XSS-skydd med DOMPurify\n- 🔒 Säkra sessioner och cookies\n- 📋 Starka lösenordskrav (12+ tecken, blandade case, siffror)\n- 🏗️ Security headers med Helmet (CSP, HSTS, etc.)\n\n**Förbättringar:**\n- ♻️ Komplett omskrivning av backend för säkerhet\n- 🎨 Modulär CSS-arkitektur (6 filer)\n- 📱 Responsiv design för mobila enheter\n- ⚡ Optimerad bildhantering med Sharp\n- 🚀 Cachad rendering för snabbare UI\n- 📊 Komplett dokumentation (1,500+ rader)\n\n**Arkitektur:**\n- 🗄️ SQLite-databas med auto-migration\n- 🔌 WebSocket för real-time updates\n- 📦 Session-based autentisering\n- 🐳 Docker-support\n\n## 📄 Licens\n\nMIT License - Se [LICENSE](./LICENSE) för detaljer.\n\nCopyright (c) 2025 Keep Clone Contributors\n\n## 🤝 Bidra\n\n**Detta är ett hobbyprojekt som tillhandahålls \"SOM DET ÄR\" under MIT-licens.**\n\nJag delar koden fritt, men jag förbinder mig inte att granska PRs, fixa buggar eller implementera funktioner. Om du hittar problem eller vill ha förbättringar, är du välkommen att:\n\n- **Forka projektet** och modifiera det för dina behov\n- **Dela dina förbättringar** med communityn (men ingen skyldighet för mig att merga)\n- **Hjälpa andra användare** i diskussioner om du vill\n\nIngen support eller underhåll garanteras. Detta är ett personligt projekt jag byggde för min familjs användning.\n\n## 💡 Planerade funktioner\n\n- [ ] Etiketter/taggar för organisering\n- [ ] Påminnelser\n- [ ] Bilagor på nya anteckningar (inte bara import)\n- [ ] Markdown-stöd\n- [ ] Export till olika format (PDF, Markdown)\n- [ ] Mobil app (PWA)\n- [ ] Två-faktor autentisering\n- [ ] Backup-schema\n- [ ] Samarbetsredigering med cursor-sync\n\n## ❓ Support\n\n**Ingen support tillhandahålls för detta hobbyprojekt.**\n\nKoden delas \"som den är\" utan någon garanti för fixar eller svar. Du kan dock:\n\n1. **Läs dokumentationen** i detta repo - den är omfattande\n2. **Sök bland existerande issues** - någon kan ha löst ditt problem\n3. **Hjälp varandra** - diskussioner i communityn är välkomna\n4. **Forka och fixa** - du har full tillgång att modifiera koden\n\nJag byggde detta för min familj och delar det i hopp om att det hjälper andra, men jag kan inte förbinda mig att ge support eller buggfixar.\n\n## 👨‍👩‍👧‍👦 För familjer\n\nKeep Clone är särskilt designad för familjer som vill:\n- 🏠 Ha full kontroll över sina data\n- 🔒 Inte låta Google läsa deras anteckningar\n- 💰 Spara pengar (helt gratis, öppen källkod)\n- 🤝 Enkelt dela anteckningar med familjen\n- 📱 Synkronisera mellan alla enheter\n- 🚀 Enkelt sätta upp på hemmaserver eller NAS\n- 🛡️ Ha företagssäkerhet utan företagskostnad\n\n**Perfekt för:**\n- Inköpslistor\n- Recept\n- Todolistor\n- Familjeplanering\n- Reseplaner\n- Anteckningar från möten\n- Idéer och brainstorming\n- Lösenord och viktiga noteringar\n\n---\n\n**Byggd med ❤️ för familjer som värdesätter integritet och enkelhet.**\n\n**Version 1.1.0** | [Changelog](#changelog-1) | [Licens](./LICENSE) | [Dokumentation](#dokumentation)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgillinger%2Fkeep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcgillinger%2Fkeep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgillinger%2Fkeep/lists"}