Skip to content

sasuke0787/sascal

Repository files navigation

SasCal

A self-hosted Calendly alternative for single-user scheduling. Built with SvelteKit, Supabase, and Google Calendar API.

Features

  • Single-user scheduling - Designed for individuals who want to share their availability
  • Google Calendar integration - Syncs with multiple Google accounts to check availability
  • Magic link authentication - Secure, passwordless admin access
  • Customizable availability - Weekly schedules with date overrides
  • Multiple event types - Create different meeting types (30 min call, 1 hour consultation, etc.)
  • Booking management - View, filter, and cancel bookings
  • Automatic calendar events - Creates Google Calendar events on booking
  • Cancellation flow - Guests can cancel their own bookings

Tech Stack

  • Frontend: SvelteKit 2 + Svelte 5 + Tailwind CSS 4
  • Database: Supabase (PostgreSQL + Auth)
  • Calendar: Google Calendar API
  • Hosting: Netlify
  • Email: Resend (optional, for confirmations)

Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Public Pages  │     │   Admin Pages   │     │  Google Calendar│
│  (Booking Flow) │     │  (Management)   │     │      API        │
└────────┬────────┘     └────────┬────────┘     └────────┬────────┘
         │                       │                       │
         └───────────┬───────────┴───────────────────────┘
                     │
              ┌──────┴──────┐
              │  SvelteKit  │
              │   Server    │
              └──────┬──────┘
                     │
              ┌──────┴──────┐
              │  Supabase   │
              │  (DB+Auth)  │
              └─────────────┘

Quick Start

1. Clone and Install

git clone <your-repo>
cd sascal
npm install

2. Set Up Supabase

  1. Create a new project at supabase.com
  2. Run the migration in supabase/migrations/001_initial_schema.sql via the SQL Editor
  3. Copy your project URL and keys from Settings > API

3. Set Up Google OAuth

  1. Go to Google Cloud Console
  2. Create a new project (or select existing)
  3. Enable the Google Calendar API
  4. Configure OAuth consent screen
  5. Create OAuth 2.0 credentials (Web application)
  6. Add authorized redirect URI: http://localhost:5173/api/google/callback

4. Configure Environment

cp .env.example .env

Edit .env with your values:

# Supabase
PUBLIC_SUPABASE_URL=https://your-project.supabase.co
PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# Owner (single user allowed to access admin)
OWNER_EMAIL=your@email.com

# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Encryption (generate with: openssl rand -hex 32)
TOKEN_ENCRYPTION_KEY=your-32-byte-hex-key

# Resend Email (optional)
RESEND_API_KEY=your-resend-api-key

5. Run Development Server

npm run dev

Visit http://localhost:5173

Project Structure

src/
├── lib/
│   ├── availability.ts          # Slot calculation algorithm
│   ├── crypto.ts                 # Token encryption (AES-256-GCM)
│   ├── google/
│   │   ├── oauth.ts              # OAuth flow
│   │   └── calendar.ts           # Calendar API helpers
│   └── supabase/
│       ├── client.ts             # Browser client
│       ├── server.ts             # Server client
│       └── types.ts              # Database types
├── routes/
│   ├── +page.svelte              # Homepage (list event types)
│   ├── [eventTypeSlug]/          # Public booking page
│   ├── confirm/[bookingId]/      # Booking confirmation
│   ├── cancel/[bookingId]/       # Cancellation page
│   ├── auth/
│   │   ├── login/                # Magic link login
│   │   ├── callback/             # Auth callback
│   │   └── unauthorized/         # Access denied
│   ├── admin/
│   │   ├── +page.svelte          # Dashboard
│   │   ├── event-types/          # Manage event types
│   │   ├── availability/         # Set weekly schedule
│   │   ├── calendars/            # Connect Google accounts
│   │   ├── bookings/             # View/manage bookings
│   │   └── settings/             # General settings
│   └── api/
│       ├── google/               # OAuth endpoints
│       └── availability/         # Slot calculation API
└── hooks.server.ts               # Auth middleware

Database Schema

Table Description
owner_settings Timezone, buffer times, booking windows
google_accounts Connected Google accounts (encrypted tokens)
availability_windows Weekly recurring hours
event_types Meeting types
bookings Scheduled meetings
date_overrides Block specific dates or set custom hours

Availability Algorithm

When calculating available slots for a date:

  1. Get weekly availability windows for that day of week
  2. Check for date overrides (blocked or custom hours)
  3. Fetch busy times from ALL connected Google Calendars
  4. Fetch existing bookings from database
  5. Generate 15-minute slots within availability windows
  6. Filter out slots that:
    • Conflict with busy times (including buffers)
    • Don't meet minimum notice requirement
    • Extend past max advance days
  7. Return available slots

Admin Pages

Route Purpose
/admin Dashboard with stats and upcoming bookings
/admin/event-types Create/edit meeting types
/admin/availability Set weekly schedule and date overrides
/admin/calendars Connect/disconnect Google accounts
/admin/bookings View and cancel bookings
/admin/settings Timezone, notice periods, buffers

Security

  • Token encryption: Google OAuth tokens are encrypted with AES-256-GCM before storage
  • Single-user auth: Only the configured OWNER_EMAIL can access admin
  • Race condition protection: Slot availability is re-checked before booking
  • Row Level Security: Supabase RLS policies on all tables

Deployment (Netlify)

  1. Push to GitHub
  2. Connect repo to Netlify
  3. Set build command: npm run build
  4. Set publish directory: build
  5. Add all environment variables
  6. Update Google OAuth redirect URI to production URL

Scripts

npm run dev       # Start dev server
npm run build     # Build for production
npm run preview   # Preview production build
npm run check     # TypeScript check

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published