Synaplan
Open-source AI platform for companies: self-hosted AI agents, GDPR-compliant chatbot widget, RAG knowledge base, WhatsApp and Outlook AI, MCP, multi-model routing. Docker, Kubernetes or hosted in Germ
Kalendarz rezerwacji mieszkań na wynajem krótkoterminowy.
Translation pending
BusinessGitHub @WeedfiOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Booking calendars for a short-term rental manager and the owners of the apartments they run. The manager sees every property on one live tape chart; each owner signs in and sees only their own. Bookings come from Booking.com and Airbnb iCal feeds, and a Booking.com email triggers a sync within seconds.
Built with Next.js 16, Supabase (Postgres, Auth, Row Level Security, Realtime) and Vercel, on free tiers only.
Live demo: https://booking-calendar-demo.vercel.app. Fake data; sign in with one click as the manager or an owner.
| Owner view (phone) | Admin on a phone | Managing properties |
|---|---|---|
Manager (admin)
Owner
flowchart LR
subgraph Channels
BK[Booking.com]
AB[Airbnb]
end
BK -- "notification email" --> MB[Manager's mailbox]
MB -- "auto-forward" --> RS["Resend inbound<br/>(or Cloudflare Worker)"]
RS -- "signed webhook<br/>/api/inbound/resend" --> APP
CRON["Supabase pg_cron<br/>every 5 min"] -- "GET /api/cron/sync<br/>(bearer secret)" --> APP
ADM[Admin: Refresh now] --> APP
subgraph Vercel
APP["Next.js app<br/>sync service"]
end
APP -- "fetch .ics" --> BK
APP -- "fetch .ics" --> AB
APP -- "upsert / cancel<br/>(secret key)" --> DB[(Supabase Postgres<br/>RLS)]
DB -- "Realtime<br/>(RLS applied)" --> UI[Admin and owner browsers]
UI -- "reads as the signed-in user" --> DB
.ics feed and parse its events with node-ical. DTEND is the checkout day and stays exclusive.cancelled. Past and in-progress stays are kept, because feeds drop old events on their own.last_synced_at or last_sync_error on the channel, and log the run in sync_events."Today" is computed in Europe/Warsaw, where the apartments are, not in the server's UTC.
| Trigger | When | Notes |
|---|---|---|
| Seconds after Booking.com emails the manager | Finds the apartment by Booking.com property ID; when several apartments share that property (one room type each), the room name in the email decides. Syncs everything if unsure. Debounced for 20 s, with one follow-up sync 45 s later because Booking can update the feed after the email. | |
| Cron | Every 5 minutes (Supabase pg_cron + pg_net) | The safety net. The Vercel free plan only allows daily cron jobs, and GitHub Actions schedules can run hours late, so the database schedules it. A GitHub workflow stays as a backup. |
| Manual | "Refresh now" | For the whole portfolio or a single property. |
channels, which hold the iCal URLs, or to sync_events. Tests sign in as real users and prove that one owner cannot read another's data, including over Realtime (rls.test.ts, realtime.test.ts).ical.booking.com/…a1b2, and sync errors never contain the URL.owner, whatever the signup metadata says.guest_stays) with RLS (admin writes; owners read their own apartments only), and a hand-entered name is never overwritten by an email. Email bodies are never stored; look-alike sender domains are rejected.Next.js 16 (App Router, Server Actions, after()), TypeScript, Tailwind CSS 4 · Supabase Postgres, Auth (magic link + password), RLS, Realtime · node-ical · Vitest · GitHub Actions · Cloudflare Email Workers.
src/
app/ routes: /admin, /admin/properties, /admin/owners, /owner, /login, /api/*
components/ header, forms, live updates
lib/
sync/ iCal fetch, parse, diff, run, Supabase store, demo feeds
email-trigger/ payload, sender check, property matching, handler
calendar/ tape chart layout, occupancy, filters
admin/ owner/ page view models and data loading
realtime/ change -> toast mapping
supabase/
migrations/ schema, RLS policies, Realtime
seed.sql fake demo data
templates/ magic link and invite emails
tests/db/ integration tests against local Supabase
workers/email-trigger/ Cloudflare Email Worker
Most logic lives in pure functions with unit tests next to them. Pages only load data and render.
Requirements: Node 24 and Docker Desktop.
npm install
npx supabase start # local Postgres, Auth, Realtime, Mailpit
cp .env.example .env.local # then fill in the values from `npx supabase status -o env`
npm run sync # import the demo feeds into the seed data
npm run dev
Open http://127.0.0.1:3000 and sign in as [email protected] (or the owner [email protected]). The magic link arrives in Mailpit at http://127.0.0.1:54324.
The seed data points at demo:// feeds, which the app generates itself when DEMO_FEEDS=true. The whole sync pipeline runs for real without any Booking.com account, and one feed fails on purpose to show what sync errors look like.
| Command | What it does |
|---|---|
npm test | Unit tests |
npm run test:db | Integration tests (RLS, sync, Realtime); needs supabase start |
npm run lint / npm run typecheck | Static checks |
npm run db:reset | Recreate the local database from migrations and seed, then run npm run sync |
npm run db:types | Regenerate src/lib/database.types.ts |
npx supabase link and npx supabase db push. Add --include-seed only for a demo with fake data.
https://<your-app>/** to the redirect URLs.supabase/config.toml. The sign-in link goes to /auth/confirm?token_hash=…; the invitation goes to /invite?token={{ .Data.invite_token }}, a link that never expires and works until the invitation is accepted. Under Security notifications, turn on "Password changed" with password_changed_notification.html.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEYCRON_SECRET, SYNC_TRIGGER_SECRET (long random strings)DEMO_MODE=true and DEMO_FEEDS=trueapp_url (your Vercel URL) and cron_secret (same value as CRON_SECRET). The sync-calendars pg_cron job (migration) then calls the sync every 5 minutes. Optionally, also add the GitHub repository variable and secret for the backup .With DEMO_MODE=true, the login page offers one-click sign-in as the demo manager or owner, and every change to properties, channels and owners is refused. Syncing, filters and live updates all keep working. Never enable it on a deployment with real data.
sync_events, not in memory, and the follow-up sync runs in after() within the free plan's 60 s limit.APP_URLCRON_SECRET…@<id>.resend.app address, add a Resend webhook for email.received pointing to /api/inbound/resend, and set RESEND_API_KEY (full access) and RESEND_WEBHOOK_SECRET in Vercel. A Cloudflare Email Worker alternative is in workers/email-trigger.