Skip to content

Repository files navigation

Dandy Dashboard

A personal, modular dashboard built with a Go backend and Svelte frontend. Drop in the widgets you want — adding a new one is a few files each side.

Right now, this is entirely codified as opposed to pluggable within the browser.

This was made in conjunction with an AI Agent and purely being done as a way to refresh and relearn newer frameworks in conjunction with understanding the current agent model capabilities.

Dashboard screenshot placeholder

Widgets

WidgetDescription
Word of the DayDaily Japanese vocabulary pulled from your WaniKani Apprentice/Guru items, with readings, meanings, and example sentences
Upcoming EventsNext events from Google Calendar via a service account
Claude AIStreaming chat with Claude Opus 4.6, with server-side conversation history and adaptive thinking

Tech Stack

  • Backend — Go 1.25, stdlib net/http (no framework)
  • FrontendSvelte 5 + TypeScript + Vite + Tailwind CSS
  • AIAnthropic API (Claude Opus 4.6, streaming SSE)
  • Calendar — Google Calendar API (service account)
  • Persistencebbolt embedded KV (default) or Redis

Getting Started

1. Clone & configure

git clone https://github.com/dandydeveloper/dandy-dashboard
cd dandy-dashboard
cp .env.example .env
# Edit .env — ANTHROPIC_API_KEY is required at minimum

2. Run in development

Terminal 1 — backend (with hot reload via air):

make dev-backend

Terminal 2 — frontend (Vite HMR):

make dev-frontend

Open http://localhost:5173.

3. Production with Docker

docker compose up --build

Frontend is served by Nginx on port 80. The backend API is internal — Nginx proxies /api/* to it.

Configuration

All configuration is via environment variables (.env file locally, secrets manager in production).

VariableRequiredDefaultDescription
ANTHROPIC_API_KEYYesFrom console.anthropic.com
WANIKANI_API_TOKENNoFrom wanikani.com/settings/personal_access_tokens — enables WaniKani word source
GOOGLE_CREDENTIALS_JSONNoPath to service account JSON file, or raw JSON string
GOOGLE_CALENDAR_IDNoprimaryCalendar ID from Google Calendar settings
PORTNo8080Backend listen port
ALLOWED_ORIGINSNohttp://localhost:5173Comma-separated CORS origins
DASHBOARD_KEYNoShared secret for X-Dashboard-Key header — set this in production
DATA_DIRNo./dataDirectory for the embedded bbolt database
STORE_URLNoRedis URL (e.g. redis://localhost:6379) — overrides bbolt

Google Calendar setup

  1. Create a project in Google Cloud Console
  2. Enable the Google Calendar API
  3. Create a Service Account — no IAM role needed
  4. Download the JSON key → set as GOOGLE_CREDENTIALS_JSON
  5. In Google Calendar, share your calendar with the service account email → "See all event details"

Adding a Widget

The plugin system is explicit — no magic. A few files each side.

Backend — create internal/widgets/mywidget/:

// widget.go — implement the Widget interfacefunc (w*Widget) Slug() string { return"mywidget" }
func (w*Widget) RegisterRoutes(mux*http.ServeMux) {
mux.HandleFunc("GET /data", w.handler.Data)
}

Then register in cmd/server/main.go:

registry.Register(mywidget.New(cfg))

Frontend — create web/src/widgets/mywidget/:

// index.tsexportconstmyWidget: WidgetDescriptor={id: 'mywidget',title: 'My Widget',description: '...',component: MyWidgetComponent,defaultSize: 'md',}

Then add one line to web/src/widgets/registry.ts:

exportconstwidgetRegistry=[japaneseWidget,calendarWidget,myWidget]

The widget appears in the resizable grid automatically.

Project Structure

dandy-dashboard/
├── cmd/server/main.go # Entry point — wires config, registry, server
├── internal/
│ ├── config/ # Env-based configuration
│ ├── httputil/ # JSON response helpers
│ ├── middleware/ # Logger, recover, request ID, CORS, API key
│ ├── store/ # bbolt / Redis abstraction
│ ├── widget/ # Widget interface + registry
│ └── widgets/
│ ├── claude/ # Claude AI chat (streaming SSE, session management)
│ ├── japanese/ # Word of the day (WaniKani + Jotoba)
│ └── calendar/ # Google Calendar events
├── docker/
│ ├── backend.Dockerfile
│ ├── frontend.Dockerfile
│ └── nginx.conf # Serves static files, proxies /api/* to backend
└── web/src/
├── widgets/
│ ├── types.ts # WidgetDescriptor interface
│ ├── registry.ts # All active widgets registered here
│ ├── claude/
│ ├── japanese/
│ └── calendar/
├── components/
│ ├── DashboardGrid.svelte # Resizable 12-column grid (layout persisted to localStorage)
│ └── WidgetCard.svelte # Shared card shell
└── stores/
└── layout.ts # Grid layout state + localStorage persistence

Security

  • API endpoints are optionally protected by a shared X-Dashboard-Key header (DASHBOARD_KEY env var) — enable this if the dashboard is publicly reachable
  • CORS is restricted to ALLOWED_ORIGINS
  • Request bodies are capped at 512 KB; messages at 32 KB
  • Chat sessions expire after 24 hours of inactivity
  • Containers run as non-root
  • Raw errors from external APIs are never forwarded to the client

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages