The product
Paphos Community brings three workflows into one cross-platform product: event discovery and RSVP, community-group discovery, and a local services marketplace. The Expo client is a single Expo Router app configured for iOS, Android, and static web output. A Hono API exposes the domain model and runs on Cloudflare Workers with D1 and KV bindings.
Event discovery and RSVP
The event feed returns upcoming meetups in date order. Each event carries a date, time, duration, location, optional map URL, description, minimum and maximum participant targets, participant records, and comments. The card derives a status from the event date and minimum target: Confirmed when enough people have joined, Needs people when it still needs participants.
Event details and RSVP
The details view brings the event description, external map link, participant list, capacity information, and comments together. The RSVP flow supports both named anonymous participants and authenticated users, lets a participant set a guest count, and enforces maxParticipants in the API. Event creators and admins/moderators can edit or cancel an event; authenticated users can add comments.


Passwordless authentication
Authentication uses email OTP instead of passwords. The flow sends a six-digit code through Resend, stores it in Cloudflare KV for 15 minutes, and returns JWT access and refresh tokens after verification. After a 401, the client retries the request with a mutex-guarded refresh. The backend rotates refresh tokens and stores used token IDs in a KV blacklist. Access tokens expire after 15 minutes, while refresh tokens last one year.

Community groups directory
The groups directory comes from stored data rather than hard-coded entries. Each record has a name, description, and external link. The client filters by name or description and supports Popular, A to Z, and Newest sorting. It records a click before opening the external link and shows add/edit controls to admins and moderators. Popular sorting uses a time-weighted score based on recorded group clicks instead of a static order.

Services marketplace
The services section adds structured listings for local providers. Each one can include a category, business name, title, description, location and coverage area, contact details, rate type/range, and up to eight images. New listings enter PENDING_REVIEW. Only ACTIVE listings are public, and admins or moderators can approve, reject, or remove them.
Edits to an active listing do not overwrite the published record immediately. They are stored as a pending edit with a revisionId, and moderation only applies the revision if it is still the one the reviewer saw. Applying an approved edit increments contentVersion, so a stale moderation screen cannot silently publish over newer content. Active listings can also carry a paginated update feed.
Architecture
The project is a TypeScript workspace with an Expo client, a Hono API, and Cloudflare Pages Functions for web-facing SEO and share previews.
Frontend: The Expo Router client shares one navigation and UI layer across iOS, Android, and web. NativeWind supplies Tailwind-style React Native classes. TanStack Query handles server-state fetching and cache invalidation, while React Hook Form and Zod validate input. The services form uses the same Zod schemas as the API.
Backend: A Hono REST API runs on Cloudflare Workers. Drizzle ORM provides access to Cloudflare D1. KV stores OTPs, refresh-token blacklist entries, and rate-limit counters. Resend sends authentication and moderation emails. Role-based access control distinguishes ADMIN, MOD, and USER, with owner, admin, and moderator checks around event, group, and service management.
Media and edge previews: Service uploads use Cloudflare Images instead of proxying image bytes through the API. The Worker creates a direct-upload URL and records image ownership. The Expo client prepares a JPEG and uploads it directly to Cloudflare, and public cards resolve imagedelivery.net URLs. Cloudflare Pages Functions generate 1200×630 PNG previews for events and active service listings. At the edge, @cf-wasm/satori builds the SVG and @cf-wasm/resvg renders the PNG. The response is cached for one hour, and service previews can include the listing’s primary Cloudflare Images photo.
Testing: Both the backend and Expo packages expose Vitest commands. The suites cover authentication and error handling, service schemas and route guards, version/revision conflicts, Cloudflare Images helpers, API and auth behavior, component flows, image preparation, and SEO schema builders.
