Skip to content

flaccid/google-keep-clone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

💡 Google Keep Clone

A research & development project mirroring the official Google Keep REST API — built with Goa (Go), Next.js 16, and PostgreSQL.

CI Go Version Node Version Next.js License: Apache 2.0 GitHub Release

Google Keep Clone — note grid with colored cards, sidebar navigation, and search


✨ Features

Feature Status
📝 Create, edit, delete notes
📋 Checklist / list notes
🎨 Solid color backgrounds (10 colours)
🌈 Gradient theme backgrounds (8 themes)
📌 Pin / unpin notes
🗂️ Archive / unarchive notes
🗑️ Trash / restore / permanent delete
🏷️ Label management (CRUD + rename)
🔍 Full-text search (ILIKE)
🌙 Dark mode (persisted to localStorage)
📎 Attachment upload & download
🔗 Share permissions (batch create/delete)
🧭 Sidebar navigation (collapsible mini mode)
☸️ Helm chart + ArgoCD deployment
🏠 Kubernetes deployment (with OAuth2 proxy)
📖 OpenAPI 3.0 docs (Swagger UI)

🏗️ Tech Stack

Backend

  • Go 1.25 with Goa v3.28 (design-first API framework)
  • PostgreSQL 16 with pgx/v5 connection pool
  • golang-migrate/v4 for schema migrations
  • Goa CLUE for structured logging & debugging
  • go:embed for OpenAPI spec bundling

Frontend

  • Next.js 16 (App Router, standalone output)
  • React 19 with TypeScript 5
  • Tailwind CSS v4 (no config file — @import "tailwindcss" in globals.css)
  • Lucide React icons
  • clsx for conditional classNames

Infrastructure

  • Docker Compose (local dev)
  • Helm chart (recommended deploy)
  • ArgoCD GitOps deployment
  • Kubernetes manifests (legacy)
  • OAuth2 Proxy with Google provider
  • GitHub Actions CI (lint → build → test → Docker push)

🗺️ Project Structure

.
├── backend/                       # Go module: github.com/flaccid/google-keep-clone/backend
│   ├── design/                    # Goa DSL — edit these first
│   │   ├── design.go              #   API metadata & server config
│   │   ├── types.go               #   Data types (Note, Section, ListItem, etc.)
│   │   ├── notes.go               #   Notes service (14 methods)
│   │   ├── labels.go              #   Labels service (CRUD + update)
│   │   ├── permissions.go         #   Permissions service (batch create/delete)
│   │   └── media.go               #   Media service (download)
│   ├── gen/                       # 🔒 Auto-generated Goa code — never edit
│   ├── api/                       # Business logic
│   │   ├── notes.go
│   │   ├── labels.go
│   │   ├── permissions.go
│   │   └── media.go
│   ├── store/                     # PostgreSQL data layer
│   │   ├── db.go                  #   pgx/v5 connection pool
│   │   ├── notes.go               #   Note CRUD, search, label join
│   │   ├── labels.go              #   Label CRUD
│   │   ├── permissions.go         #   Permission queries
│   │   ├── attachments.go         #   File-based attachment store
│   │   ├── migrations/            #   SQL migration files
│   │   ├── migrate.go             #   Auto-migration runner
│   │   ├── notes_test.go          #   Integration tests (17 tests)
│   │   ├── labels_test.go         #   Integration tests (3 tests)
│   │   └── permissions_test.go    #   Integration tests (5 tests)
│   ├── cmd/keep_server/           # Server entrypoint
│   │   ├── main.go                #   Flags, DB init, service wiring
│   │   ├── http.go                #   HTTP server, upload handler, OpenAPI handler
│   │   └── openapi3.yaml          #   📎 Embedded OpenAPI spec
│   ├── Makefile                   #   `make gen` → goa gen + spec copy
│   ├── Dockerfile                 #   Multi-stage Go build → alpine
│   ├── go.mod                     #   Go 1.25.0
│   └── go.sum
├── frontend/                      # Next.js 16 (App Router, TypeScript, Tailwind v4)
│   ├── src/
│   │   ├── app/                   # App Router pages
│   │   │   ├── page.tsx           #   Home / note grid
│   │   │   ├── archive/page.tsx   #   Archived notes
│   │   │   ├── trash/page.tsx     #   Trashed notes
│   │   │   ├── reminders/page.tsx #   Reminders (placeholder)
│   │   │   └── labels/
│   │   │       ├── page.tsx       #   Label manager (inline rename/delete)
│   │   │       └── [id]/page.tsx  #   Notes filtered by label
│   │   ├── components/            # React components
│   │   │   ├── Header.tsx         #   Fixed header with lightbulb icon
│   │   │   ├── Sidebar.tsx        #   Collapsible sidebar (mini mode)
│   │   │   ├── NoteEditor.tsx     #   Inline "Take a note..." + label picker
│   │   │   ├── NoteCard.tsx       #   Note display card + color/theme palette
│   │   │   ├── NoteModal.tsx      #   Full-note overlay editor
│   │   │   ├── Palette.tsx        #   Shared color/theme picker
│   │   │   └── ThemeProvider.tsx  #   Dark mode context + localStorage
│   │   └── lib/
│   │       ├── api.ts             #   Full API client (notes, labels, permissions)
│   │       └── types.ts           #   TypeScript interfaces
│   ├── next.config.ts             #   Rewrites: /v1/* → backend
│   ├── Dockerfile                 #   Multi-stage Next.js standalone
│   └── package.json               #   Next.js 16, React 19, Tailwind v4
├── charts/                        # Helm chart (recommended deploy)
├── k8s/                           # Kubernetes manifests (legacy)
├── docs/                          # getting-started.md, api-reference.md
├── docker-compose.yml             # postgres + backend + frontend
└── .github/workflows/ci.yml       # Lint → Build → Test → Docker push

See the full API reference for all endpoints, query parameters, note body types, colour values, and usage examples. Interactive docs are also available at /openapi (Swagger UI) when running the app.


🚀 Quick Start

Kubernetes (recommended)

See the getting-started guide for cluster setup, OAuth2 configuration, Helm install, and ArgoCD deployment.

Docker Compose

docker compose up

This starts three services:

  1. PostgreSQL 16 on port 5432
  2. Backend on port 8080 (auto-migrates DB on startup)
  3. Frontend on port 3000

Visit http://localhost:3000 — all API calls are proxied from the Next.js dev server to the Go backend.

Local development (without Docker)

# Terminal 1 — Database
docker run -d --name keep-pg \
  -e POSTGRES_USER=keep \
  -e POSTGRES_PASSWORD=keep \
  -e POSTGRES_DB=keep \
  -p 5432:5432 \
  postgres:16-alpine

# Terminal 2 — Backend
cd backend
DATABASE_URL="postgres://keep:keep@localhost:5432/keep?sslmode=disable" \
  go run ./cmd/keep_server -host=localhost

# Terminal 3 — Frontend
cd frontend
npm install
npm run dev

🧑‍💻 Development Workflow

Backend — Goa codegen cycle

cd backend

# 1. Edit the DSL in design/*.go
# 2. Regenerate Goa code + copy OpenAPI spec
make gen

# 3. Update business logic in api/*.go
# 4. Build & verify
go build ./...
go vet ./...

# 5. Run store integration tests (requires PostgreSQL)
DATABASE_URL="postgres://keep:keep@localhost:5432/keep?sslmode=disable" \
  go test -count=1 ./store/... -v

Rules:

  • gen/ is auto-generated — never edit manually
  • Route params use mux.Vars(r) (Goa muxer), not r.PathValue
  • The DB auto-migrates on startup — no manual migration step needed
  • After make gen, always sync cmd/keep_server/openapi3.yaml (already done by the Makefile)

Frontend

cd frontend
npm install
npm run dev     # Development server on :3000
npm run build   # TypeScript check + production build
npm run lint    # ESLint

Key details:

  • API calls use relative paths (/v1/notes, etc.) — CORS is not needed
  • Next.js rewrites proxy /v1/*, /openapi, /openapi.yaml to the backend
  • API_UPSTREAM_URL is a build-time ARG baked into rewrites, not a runtime env var
  • Tailwind v4 is configured via @import "tailwindcss" + @theme {} in globals.css — no tailwind.config.ts or postcss.config.ts

☸️ Kubernetes Deployment

The recommended way is via the Helm chart — see the getting-started guide for complete instructions with Helm CLI or ArgoCD.

You can also apply the raw k8s/ manifests (legacy) — note these lack the chart's value overrides, secret generation, and network policy egress rules.

Components (all in namespace google-keep-clone):

Component Type Details
PostgreSQL StatefulSet + Service PersistentVolumeClaim, init container wait
Backend Deployment + Service + HPA 100m CPU / 128Mi requests, auto-migration
Frontend Deployment + Service Standalone Next.js output
OAuth2 Proxy Deployment + Service Google OAuth, email whitelist
Ingress 2 Ingress resources Main (auth-protected) + /oauth2 (unauthenticated)

🔄 CI/CD Pipeline

GitHub Actions runs on every push/PR to main:

  1. Lintgo vet ./... + govulncheck (continue-on-error)
  2. Build & Testgo build ./... + store integration tests (with PostgreSQL container)
  3. Frontendnpm ci + npm audit --audit-level=moderate (continue-on-error) + npm run build
  4. Docker push — On main only, pushes flaccid/google-keep-clone-{backend,frontend}:latest and :{sha}

🎨 UI Highlights

  • Sidebar: Collapsed = icons-only 68px strip; hover temporarily expands with shadow overlay; hamburger toggles expanded/collapsed; main content margin adjusts accordingly
  • Color palette: Click-to-toggle dropdown with two sections — Themes (gradient swatches) and Colors (solid circles)
  • Dark mode: Toggle in settings dropdown, persists to localStorage, class-based via Tailwind dark: variant
  • Note editor: Inline "Take a note..." with expand, text/list toggle, label picker, color/theme palette
  • Label manager: Inline rename (pencil icon), delete with confirmation, sidebar "Edit labels" link
  • Labels on notes: Note.labels contains display name strings (not resource names); label filter page fetches all labels and matches by displayName

🔒 Security Notes

Authentication

This is an R&D project — authentication is not yet implemented. The API currently has no user isolation: anyone who can reach the backend can read, create, modify, or delete any note. The database schema includes a permissions table for future multi-user support, but no access control logic exists yet.

For local development with Docker Compose, the backend is only accessible via the Next.js proxy on localhost:3000. For Kubernetes deployments, OAuth2 proxy provides authentication at the ingress level (see Kubernetes Deployment).

TLS / HTTPS

The Go backend serves plain HTTP on port 8080. In Kubernetes deployments, TLS is terminated at the ingress (nginx-ingress with OAuth2 proxy). In local development with Docker Compose, traffic stays on localhost. The backend does not serve HTTPS directly.

Dependency Management

Security vulnerabilities are tracked via CI tooling (npm audit, govulncheck) and SECURITY.md (kept local, not tracked in git).


🗄️ Database Schema

Table Purpose
notes Core note data (title, body_text, color, pinned, archived, trashed)
list_items Checklist items (linked to notes, ordered, with checked state)
labels Label definitions (name, hex color)
note_labels Many-to-many join between notes and labels
permissions Note sharing (email, role, type)
attachments File metadata (filename, size, mime type, linked to notes)

Migrations live in backend/store/migrations/ and are embedded into the binary via //go:embed.


🤝 Contributing

This is a personal R&D project, but issues and PRs are welcome! Feel free to open a discussion for major changes first.


License and Authors

Copyright 2026, Chris Fordham

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.