Skip to content

Latest commit

Β 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🍡 Chamomile

A step up on my previous kanban board project with user authentication, persistent database and type safety. Built with TypeScript, Express and SQLite.

Chamomile Login

Chamomile App

Railway Link: chamomile-production.up.railway.app/

✨ Features

  • πŸ” Secure Authentication - JWT-based auth with bcrypt password hashing
  • πŸ€– Anti-Bot Protection - Honeypot fields and timing analysis
  • πŸ’Ύ Persistent Storage - SQLite database with automatic backups
  • πŸš€ Type-Safe - Full TypeScript backend with compile-time safety
  • πŸ“± Responsive - Works on desktop, tablet, and mobile, with smooth drag-and-drop

πŸ—οΈ Tech Stack

Backend:

  • TypeScript + Express
  • SQLite (better-sqlite3)
  • JWT authentication
  • bcrypt password hashing
  • express-rate-limit for DDoS protection

Frontend:

  • Vanilla JavaScript (no frameworks)
  • HTML5 drag-and-drop API
  • CSS3 animations
  • LocalStorage for token persistence

πŸ“¦ Installation

Prerequisites

  • Node.js 18+
  • npm or yarn

Setup

  1. Clone the repository
git clone https://github.com/TDJR007/Chamomile.git
cd chamomile
  1. Install dependencies
npm install
  1. Configure environment variables
cp .env.sample .env

Edit .env and set your values:

PORT=3000
JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
DB_FILE=./data/chamomile.db
NODE_ENV=development

⚠️ IMPORTANT: Change JWT_SECRET to a strong random string in production!

  1. Initialize the database
npm run db:init
  1. Start the development server
npm run dev
  1. Open your browser
http://localhost:3000

πŸš€ Production Deployment

Build for production

npm run build

This compiles TypeScript to JavaScript in the dist/ folder.

Start production server

NODE_ENV=production npm start

Environment Variables for Production

Make sure to set these in your production environment:

  • PORT - Server port (default: 3000)
  • JWT_SECRET - MUST be a strong random string
  • DB_FILE - Path to SQLite database file
  • NODE_ENV - Set to production

πŸ“ Project Structure

chamomile/
β”œβ”€β”€ public/              # Frontend files
β”‚   β”œβ”€β”€ index.html       # Main kanban board
β”‚   β”œβ”€β”€ auth.html        # Login/signup page
β”‚   β”œβ”€β”€ auth.js          # Auth logic
β”‚   β”œβ”€β”€ api.js           # API wrapper
β”‚   β”œβ”€β”€ drag.js          # Drag-and-drop
β”‚   β”œβ”€β”€ todo.js          # Task creation
β”‚   β”œβ”€β”€ utils.js         # Helper functions
β”‚   β”œβ”€β”€ storage.js       # Data loading
β”‚   β”œβ”€β”€ star-background.js
β”‚   └── styles.css
β”‚
β”œβ”€β”€ src/                             # TypeScript backend source
β”‚   β”œβ”€β”€ types/
β”‚   β”‚   └── index.ts                 # TypeScript type definitions (User, Task, etc.)
β”‚   β”‚
β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”œβ”€β”€ authMiddleware.ts        # JWT token verification middleware
β”‚   β”‚   β”œβ”€β”€ tightSignupGuard.ts      # Anti-bot protection (honeypot + timing)
β”‚   β”‚   └── errorHandler.ts          # Global error handling middleware
β”‚   β”‚
β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”œβ”€β”€ authRoutes.ts            # Auth endpoints (register, login)
β”‚   β”‚   β”œβ”€β”€ todoRoutes.ts            # Task CRUD endpoints (get, create, update, delete)
β”‚   β”‚   └── index.ts                 # Route aggregator (combines all routes)
β”‚   β”‚
β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   β”œβ”€β”€ jwt.ts                   # JWT token generation and verification
β”‚   β”‚   β”œβ”€β”€ password.ts              # Password hashing and comparison (bcrypt)
β”‚   β”‚   β”œβ”€β”€ rateLimiter.ts           # Rate limiting configurations
β”‚   β”‚   └── validation.ts            # Input validation and sanitization
β”‚   β”‚
β”‚   β”œβ”€β”€ db/
β”‚   β”‚   β”œβ”€β”€ database.ts              # SQLite database connection and operations
β”‚   β”‚   └── schemaInit.ts            # Database schema initialization script
β”‚   β”‚
β”‚   β”œβ”€β”€ app.ts                       # Express app configuration and middleware setup
β”‚   └── server.ts                    # Server entry point (starts Express server)
β”‚
β”œβ”€β”€ data/
β”‚   └── chamomile.db                 # SQLite database file (created on first run)
β”‚
β”œβ”€β”€ dist/                            # Compiled JavaScript output (after npm run build)
β”‚
β”œβ”€β”€ node_modules/                    # Dependencies (not committed to git)
β”‚
β”œβ”€β”€ .env                             # Environment variables (NOT in git)
β”œβ”€β”€ .env.sample                      # Environment variables template
β”œβ”€β”€ .gitignore                       # Files to exclude from git
β”œβ”€β”€ chamomile.rest                   # API testing file (REST Client for VS Code)
β”œβ”€β”€ nixpacks.toml                    # Railway build configuration
β”œβ”€β”€ package.json                     # Node.js dependencies and scripts
β”œβ”€β”€ package-lock.json                # Locked dependency versions
β”œβ”€β”€ railway.json                     # Railway deployment configuration
β”œβ”€β”€ README.md                        # Project documentation
└── tsconfig.json                    # TypeScript compiler configuration

πŸ”’ Security Features

Authentication

  • JWT tokens with 7-day expiration
  • Bcrypt password hashing (10 rounds)
  • Passwords must be 8+ characters

CORS Configuration

  • Same-origin policy: Our app uses origin: true which reflects the request origin; this works because frontend and backend share the same domain.
  • This is more secure than origin: '*' (allows everything) and simpler than whitelisting specific domains.

Anti-Bot Protection

  • Honeypot field - Hidden form field that bots auto-fill
  • Timing analysis - Detects forms filled too quickly
  • Rate limiting:
    • Signup: 3 attempts per 24 hours
    • Login: 10 attempts per 15 minutes
    • API: 100 requests per 15 minutes

Database Security

  • SQL injection protection via prepared statements
  • Foreign key constraints with CASCADE deletion
  • User data isolation (users can only access their own tasks)

πŸ§ͺ API Testing

Use the included chamomile.rest file with VS Code's REST Client extension:

  1. Install REST Client extension
  2. Open chamomile.rest
  3. Update the @token variable after logging in
  4. Click "Send Request" above any endpoint

Or use curl:

# Register
curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"password123","timestamp":'$(date +%s)000'}'

# Login
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"password123"}'

# Get tasks (replace YOUR_TOKEN)
curl http://localhost:3000/api/todos \
  -H "Authorization: Bearer YOUR_TOKEN"

πŸ“ API Endpoints

Authentication

  • POST /api/auth/register - Create new user
  • POST /api/auth/login - Login and get JWT token

Tasks (All require authentication)

  • GET /api/todos - Get all tasks
  • POST /api/todos - Create new task
  • PUT /api/todos/:id - Update task
  • DELETE /api/todos/:id - Delete task

πŸ› Troubleshooting

Server won't start:

  • Check that port 3000 isn't already in use
  • Verify .env file exists and has valid values
  • Run npm run db:init to ensure database is initialized

"Invalid token" errors:

  • Token may have expired (7-day limit)
  • Log out and log back in to get a new token
  • Check that JWT_SECRET is set in .env

Tasks not persisting:

  • Check data/ folder exists and is writable
  • Verify database file was created: ls data/chamomile.db
  • Check server logs for database errors

Steps to Deploy on Railway

Step 1: Push to GitHub (if not done already)

# Initialize git (if not done)
git init
git add .
git commit -m "Initial commit - Chamomile kanban board"

# Create GitHub repo, then:
git remote add origin https://github.com/yourusername/chamomile.git
git branch -M main
git push -u origin main

Step 2: Create railway.json (optional but recommended)

Create this in your project root for better control:

{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "npm start",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 10
  }
}

Step 3: Add Railway-specific build config

Railway auto-detects Node.js apps, but let's be explicit.

Create nixpacks.toml in project root:

[phases.setup]
nixPkgs = ['nodejs_20']

[phases.install]
cmds = ['npm ci']

[phases.build]
cmds = ['npm run build']

[start]
cmd = 'npm start'

This tells Railway exactly how to build your app (uses Node 20, installs deps, builds TypeScript, runs production server).


Step 4: Sign up on Railway

  1. Go to: https://railway.app
  2. Click "Login" β†’ Sign up with GitHub (easiest way)
  3. Authorize Railway to access your repos

You'll get $5 free credit immediately (no CC needed yet).


Step 5: Create a New Project

  1. Click "New Project"
  2. Select "Deploy from GitHub repo"
  3. Choose your chamomile repository
  4. Railway will auto-detect it's a Node.js app and start deploying

Step 6: Add Environment Variables

Once the initial deploy starts:

  1. Click on your service (should say "chamomile" or similar)
  2. Go to "Variables" tab
  3. Add these variables:
NODE_ENV=production
PORT=3000
DB_FILE=/app/data/chamomile.db
JWT_SECRET=<click "Generate" button or paste your own>

For JWT_SECRET: Railway has a "Generate" button that creates a secure random string. Use that!


Step 7: Add Persistent Volume for SQLite

This is CRITICAL - without this, your database resets on every deploy!

  1. Under your Project, go to "Architecture" tab
  2. Right click on your service and click "Attach Volumes"
  3. Under "Create Volume"
  4. Set:
    • Mount Path: /app/data
    • Size: 1 GB (more than enough)
  5. Click "Add"

Railway will redeploy automatically after adding the volume.


Step 8: Ensure data directory exists

Update src/db/database.ts to create the directory if it doesn't exist:

import Database from 'better-sqlite3';
import path from 'path';
import fs from 'fs';
import dotenv from 'dotenv';
import { User, Task, TaskStatus } from '../types';

// Load environment variables
dotenv.config();

const dbPath = process.env.DB_FILE || './data/chamomile.db';

// Ensure data directory exists (important for Railway/production)
const dbDir = path.dirname(dbPath);
if (!fs.existsSync(dbDir)) {
  fs.mkdirSync(dbDir, { recursive: true });
  console.log(`πŸ“ Created data directory: ${dbDir}`);
}

const db = new Database(dbPath);

// Enable foreign keys (CRITICAL for CASCADE deletion)
db.pragma('foreign_keys = ON');

console.log(`πŸ“¦ Database connected: ${path.resolve(dbPath)}`);

// ... rest stays the same

Commit and push this change:

git add .
git commit -m "Ensure data directory exists for production"
git push

Railway will auto-deploy the update.


Step 9: Get Your URL

  1. In Railway dashboard, click on your service
  2. Go to "Settings" β†’ "Networking"
  3. Click "Generate Domain"

You'll get something like:

https://chamomile-production-abc123.up.railway.app

Copy that URL and visit it! πŸŽ‰


Step 10: Test Everything

  1. Visit your Railway URL
  2. Sign up for an account
  3. Create some tasks
  4. Drag them around
  5. Refresh the page - data should persist!
  6. Log out and log back in - should still work!

πŸ”§ Railway-Specific Commands

View Logs (Real-time)

# Install Railway CLI (optional but useful)
npm i -g @railway/cli

# Login
railway login

# Link to your project
railway link

# View logs
railway logs

Or just view logs in the Railway dashboard: Service β†’ Deployments β†’ Click latest deploy β†’ View Logs


Redeploy Manually

In Railway dashboard:

  1. Go to Deployments
  2. Click "Redeploy" on latest deployment

Or push to GitHub (auto-deploys).


Check Resource Usage

Railway dashboard β†’ Metrics tab

You'll see:

  • CPU usage
  • Memory usage
  • Network bandwidth
  • Estimated cost (should be ~$3-4/month)

πŸ“Š Monitoring Your Credit

Check your usage:

  1. Railway dashboard β†’ Account Settings β†’ Usage
  2. You'll see current month's usage
  3. Breakdown by project/service

Pro tip: Set up a notification when you hit $4 (Settings β†’ Notifications).


πŸ›‘ If You Run Out of Credit

Railway will:

  1. Email you when you hit 80% of credit
  2. Pause services when credit hits $0
  3. You can add a card to continue (only charges what you use)

But realistically, $5 covers this app easily. You'd need to get significant traffic to burn through it.


πŸ“„ License

MIT License - feel free to use this for personal or commercial projects!

πŸ™ Acknowledgments

Built with Claude, TypeScript, and way too much coffee β˜•


About

A minimalist Kanban board built for Zen, productivity and peace of mind. Helps you manage tasks effortlessly while vibing under the stars. This is the step up version!

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages