An open-source guide to the architecture, hosting, database, payments, deployment and security behind Spot My Mac.
Spot My Mac is built using a modern, serverless stack. It requires no traditional servers to maintain, scaling infinitely via edge networks and serverless database providers.
The repository contains both the frontend (HTML/CSS/JS) and the backend API routes (in the /api folder). Changes are committed and pushed directly to GitHub.
Because Vercel is connected directly to the repository, pushing to the configured branch (e.g., main) automatically triggers a new deployment.
SECURITY WARNING: The repository should never contain .env files, API keys, webhook secrets, Supabase service-role keys, or any other credentials.
Vercel is a platform for frontend frameworks and static sites, built to integrate with headless content, commerce, or database systems. Here is how to deploy it:
api/ routes).Environment variables in Vercel can be scoped to different environments:
vercel dev.Vercel provides a free .vercel.app deployment URL immediately. A custom domain (such as spotmymac.com) can be connected to the project via the Domains tab. Note that after making any changes to environment variables, the project requires a new deployment (redeployment) for the new values to take effect.
Supabase provides the Postgres database for the application. You must create a Supabase project and create the required database tables.
The backend communicates with Supabase predominantly using the SUPABASE_SERVICE_ROLE_KEY in serverless API routes to bypass Row Level Security (RLS) restrictions safely. Row Level Security (RLS) should be enabled on all tables. Since operations like calculating deposits or updating payments happen server-side, no sensitive credentials are exposed to the browser client.
| Table | Purpose | What it stores |
|---|---|---|
spots |
Auction inventory | The individual sponsorship positions. In the code, this acts as the relational base for joining bids via foreign keys. |
bids |
Auction activity | Sponsor bids, brand details, submitted logos, and associated bid/payment status information. |
auction_settings |
Auction configuration | Global configuration such as the auction start and end times. |
visitors |
Live visitor tracking | Anonymous session IDs and last active timestamps to power the live visitor counter. |
If you are deploying your own instance, you will need to create the following exact schemas, as they map directly to the API logic in the /api directory.
spotsQueried indirectly via the backend /api/getSpots. The frontend codebase actually hardcodes the spot visual metadata (dimensions, sizes) and merges it with database state based on the id. The only required column is id.
bidsThis is the core table where all bids are inserted by /api/submitBid and updated by /api/webhook using the Service Role Key.
auction_settingsQueried via /api/getSettings using the public SUPABASE_ANON_KEY. Because it uses the anonymous key, this table requires an RLS policy allowing public SELECT.
visitorsUpserted dynamically by the /api/analytics serverless heartbeat using the Service Role Key.
The system relies heavily on Supabase Storage to hold the uploaded sponsor logos. You must create a public bucket named exactly logos. Without this bucket, the /api/submitBid route will throw a 500 error when attempting to push the Base64 image buffer into Storage.
CORS & Policies: Since Supabase Storage assets are accessed directly via standard image URLs in the browser (and the logo_url is saved in the bids table), you must ensure the logos bucket policies allow public anonymous reads.
Spot My Mac uses Dodo Payments to handle deposits.
This is NOT a fixed-price product. The bidder enters their total desired bid amount on the website, and the backend dynamically calculates a 20% deposit.
The backend then passes this dynamically calculated amount directly into the Dodo Checkout Session creation API. The customer does not manually type the deposit amount in Dodo. The API key used to generate this session must remain server-side so malicious users cannot generate fake sessions or modify prices.
The following variables must be configured in Vercel. Never commit actual values to GitHub.
| Variable | Purpose |
|---|---|
DODO_PAYMENTS_API_KEY |
Secret key used by the backend to create checkout sessions. |
DODO_PAYMENTS_WEBHOOK_KEY |
Secret used to verify that webhooks actually originated from Dodo. |
DODO_AUCTION_DEPOSIT_PRODUCT_ID |
The ID of the Pay What You Want product created in the Dodo dashboard. |
DODO_MODE |
Set to test_mode for development or live for production. |
APP_BASE_URL |
The URL of your deployed application (used for returning from checkout). |
SUPABASE_URL |
The URL of your Supabase project. |
SUPABASE_ANON_KEY |
Public anonymous key for non-sensitive reads. |
SUPABASE_SERVICE_ROLE_KEY |
Secret admin key used exclusively in backend routes. |
Dodo's test mode allows you to simulate successful and failed payments using test credit card numbers without moving real money. Always use test mode until you are fully ready to launch.
Webhooks are crucial because relying solely on the browser redirecting back to a "success" page is easily exploitable. A webhook is a secure, server-to-server HTTP request from Dodo telling your backend that a payment definitively succeeded.
The current application listens on /api/webhook for the payment.succeeded event. The webhook extracts the bid_id from the payment metadata and updates the corresponding bid record in Supabase to payment_status: 'paid'.
Future Improvement: While the webhook endpoint currently processes the event directly, a robust production deployment should verify the cryptographic webhook signature using the DODO_PAYMENTS_WEBHOOK_KEY to absolutely guarantee the request came from Dodo.
Here is the end-to-end user journey during the auction:
/api/submitBid.bids table, review the sponsor's details, and manually change the status column to 'approved'. This explicitly authorizes the bid to become visible in the live auction table on the frontend, locking the spot.If a payment fails, the webhook is not sent, and the bid remains stuck in a "pending" state indefinitely. If a sponsor is later outbid by a higher amount, their deposit is manually or automatically refunded depending on the Dodo configuration. Remember: Payment confirmation must come from the webhook rather than trusting the browser redirect.
Follow these steps to launch your own version:
bids, spots, visitors, auction_settings tables and the logos storage bucket).https://your-domain.com/api/webhook).test_mode.To run Spot My Mac locally, ensure you have Node.js installed.
Because the project uses Vercel Serverless Functions in the /api directory, the correct command to start the local development server is using the Vercel CLI.
Create a local .env file in the root of the project with the same keys listed above. The Vercel CLI will automatically load these variables.
Note that Dodo webhooks cannot normally reach localhost directly. During local webhook testing, you will need to use an HTTPS tunnel (like Ngrok or localtunnel) to expose your local port 3000 to the internet.
.gitignore file for this reason.To make the project your own, you will need to change the following configuration assets (safe to modify):
index.html and style.css.macbook_lid.png with your own laptop lid.spots table and API routes.index.html.You should completely understand the backend API code in /api before attempting to modify the core payment and bidding logic.
Don't trust the screenshot. Run it yourself.
The source is public and the live auction/payment flow can be thoroughly inspected and tested directly on the platform.