Skip to content

Repository files navigation

Nodate Time

CI License: AGPL-3.0 Go React MySQL Bun

A shared calendar app for running one calendar across multiple people — families, teams, talent agencies. It blends TimeTree's shared-calendar simplicity with a Google Calendar–style permission model. Monorepo: a Go REST API plus a React SPA.

Stack: Go 1.25 · Huma v2 · MySQL 8.4 · React 19 · Bun


Screenshots

Monthly view Weekly view
Monthly view with the event-create dialog Weekly timeline view
Share & embed
Share panel with invite links and embed code

Responsive mobile layout (vertical month scroll):

Mobile monthly view

Features

  • Shared calendars — per-member roles (owner / editor / viewer), invite links, public share tokens, and an embeddable (embed) view
  • Events — all-day & timed, recurrence (RRULE) with per-occurrence exception editing, drag to move/resize, attendees, checklists, attachments, comments
  • Memos — per-calendar notes
  • Albums — per-calendar photo albums (optionally linked to events)
  • Activity & audit history — change history for events/memos plus a per-calendar activity feed
  • Auth — email + password, Google / LINE OAuth/OIDC, password reset, and a development-only password-less login
  • Admin — OAuth provider configuration and an allowlist of permitted sign-in emails/domains
  • Export / import — iCal and CSV
  • i18n & themes — English / Japanese locales and theme switching

Tech stack

Area Technology
API Go 1.25 + Huma v2 (OpenAPI) + chi/v5 + sqlc
DB MySQL 8.4 (Docker Compose)
Object storage S3-compatible (MinIO locally)
Web React 19 + TypeScript + Vite + TanStack Router + Zustand + Tailwind CSS 4 + Luxon
Lint / format Biome (web) / gofmt (Go)
Package manager Bun
Testing Go E2E (testify + real MySQL) / Vitest (web)

Repo structure

apps/
  api/                 Go REST API
    cmd/api/           entrypoint
    cmd/createuser/    user-creation CLI
    internal/          config, auth, audit, cleanup, db/generated,
                       errors, mailer, recurrence, secrets, storage,
                       http/{handlers,middleware,router}
    tests/e2e/         E2E tests
  web/                 React SPA
    src/components/     UI components
    src/routes/         TanStack Router (file-based; incl. share/ and embed/)
    src/stores/         Zustand (auth / calendar / ui stores)
    src/lib/            utilities (date, recurrence, upload, theme, …)
    src/i18n/           English / Japanese locales
sql/
  tables/              table definitions (001-017)
  queries/             sqlc query definitions
  schema.sql           combined schema produced by build-schema.sh
  sqlc.yaml            sqlc config
compose.yml            local stack: MySQL + MinIO

Quick start

Prerequisites

  • Bun
  • Go 1.25+
  • Docker (for MySQL / MinIO)
  • sqlc (only when regenerating DB code)

Run

bun install            # install deps (Bun required)
make db-up             # start MySQL (compose.yml)
make db-apply          # build & apply the schema
make db-seed-users     # create demo@example.com / admin@example.com
make dev               # start DB + API + web together

In development (TC_ENV=development), /auth/dev-login provides password-less login for the seeded accounts.

Common commands

make dev               # DB + API + web in parallel
make db-up / db-down   # start / stop MySQL
make db-apply          # apply schema (build-schema.sh → schema.sql)
make db-seed           # schema + users + sample data
make minio-up          # start MinIO (S3-compatible storage)
make sqlc              # generate sqlc code
make api / make web    # run individually
make build-api         # build the API binary (bin/api)
make create-user ARGS="-email a@b.com -password secret123 -admin"
make format            # gofmt + Biome autofix
make lint              # gofmt check + Biome (no writes)
make test-api          # Go unit tests
make test-e2e          # E2E (requires MySQL + TC_TEST_INTEGRATION=1)
make test-e2e-storage  # E2E including MinIO-backed storage tests

bun run check          # Biome check
bun run typecheck      # TypeScript typecheck (tsc -b)
cd apps/web && bun run test   # web unit tests (Vitest)

Environment variables

All prefixed with TC_. Key ones below; see apps/api/internal/config/config.go for the full list and defaults.

Variable Purpose Default
TC_ENV development enables dev conveniences production
TC_PORT API port 8080
TC_DB_DSN MySQL DSN ttuser:ttpw@tcp(127.0.0.1:33306)/timetree_clone?parseTime=true
TC_JWT_SECRET JWT signing key (≥32 bytes required in prod) dev fallback
TC_PASSWORD_LOGIN_ENABLED enable email+password auth true
TC_CORS_ALLOWED_ORIGINS allowed CORS origins (no wildcard in prod) localhost:5173
TC_WEB_URL / TC_API_PUBLIC_URL public URLs for web / API localhost
TC_S3_ENDPOINT / TC_S3_ACCESS_KEY / TC_S3_SECRET_KEY / TC_S3_BUCKET / TC_S3_USE_SSL S3-compatible storage MinIO defaults
TC_SMTP_HOST and other TC_SMTP_* mail delivery (stdout when unset; required in prod) console output
TC_GOOGLE_CLIENT_ID / TC_GOOGLE_CLIENT_SECRET Google OAuth empty
TC_GOOGLE_ALLOWED_DOMAINS allowed Google sign-in domains (comma-separated) unrestricted
TC_LINE_CLIENT_ID / TC_LINE_CLIENT_SECRET LINE OAuth empty
TC_SECRETS_KEY key encrypting secrets stored in the DB (hex/base64, 32 bytes) empty

In production, Config.Validate() refuses to start with the default JWT secret, default MinIO credentials, wildcard CORS, or an unset SMTP host.

DB / storage (compose.yml): TC_DB_PORT (default 33306), TC_DB_ROOT_PASSWORD, TC_DB_NAME, TC_DB_USER, TC_DB_PASSWORD, TC_MINIO_API_PORT, TC_MINIO_CONSOLE_PORT.

API overview

Huma v2 generates the OpenAPI spec automatically. Routes live in apps/api/internal/http/router/router.go.

  • Public/health, /auth/* (register/login/dev-login/password-reset/oauth), /share/{token} (public calendar)
  • Authenticated/user, /calendars, and /calendars/{id}/{events,memos,members,invites,albums,labels,activity,export,import}
  • Admin/admin/oauth-providers, /admin/allowed-emails

Unauthenticated endpoints are rate-limited per client IP.

Testing

E2E tests create an independent user + calendar per test (tenant isolation) and run in parallel against a real MySQL.

TC_DB_PORT=3307 make db-up      # start MySQL on the same port CI uses
make test-e2e                   # runs with TC_TEST_INTEGRATION=1

CI (.github/workflows/ci.yml) runs web (Biome / typecheck / Vitest) and API (build / vet / E2E).

Migrations & code generation

  • The schema is layered. sql/core/ is a copy of the shared schema contract that lets more than one product write to the same database — it is not edited here and is pinned upstream in sql/core/UPSTREAM.json. sql/time/ holds this application's own tables.
  • sql/build-schema.sh concatenates core then time into sql/schema.sql, which make db-apply applies.
  • make test-conformance checks the resulting schema against the contract. make check-core checks that the vendored copy is unedited and current.
  • Write queries in sql/queries/*.sql and run make sqlc to generate Go code into apps/api/internal/db/generated/.

License

AGPL-3.0 — self-host, modify, and redistribute freely. Network use requires source availability.

Disclaimer & trademarks

This is an independent, original project. It is inspired by the shared-calendar experience of TimeTree, but the code, UI, and UX are its own — no source, assets, or designs are copied from any third party.

"TimeTree", "Google Calendar", and other product names are trademarks of their respective owners and are used here only for descriptive reference. This project is not affiliated with, endorsed by, or sponsored by any of them.

About

Self-hostable shared calendar app: member roles, invite links, recurring events, albums, audit history, and embeddable views. Go + Huma API and a React 19 SPA.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages