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 ↗Lamsa is an AI interior design web app. A user uploads photos of a room, describes its dimensions and door/window layout, picks a style/budget/mood, and gets back an AI-generated redesign of that same room. It also offers a lighter "quick rearrange" flow that only repositions (or fully redesigns) the furniture already visible in a single photo.
The whole app is static HTML/CSS/vanilla JS pages served by Vercel, backed by a
handful of Vercel serverless functions under api/ that call fal.ai (image
generation), Stripe (one-time credit purchases), and Upstash Redis (credit
balances).
lamsa-auth.html — login / registration screen, backed by real
server-side accounts and sessions (see Authentication).
lamsa-bilingual.html — the main designer. A 5-step form:
Submitting builds a detailed prompt from all of the above and sends it to
/api/generate, then polls /api/status until the redesigned image is ready.
Each generation spends 1 credit (see Credits & payments).
The result view also shows a "Shop similar pieces" panel — a static per-country
list of real furniture retailers with a Google Maps search link for each
(js/nearby-stores.js + data/furniture-stores.json; no paid maps API).
rearrange.html — "Quick Rearrange": upload one photo, choose Rearrange
Only (move existing furniture, add/remove nothing) or Full Redesign, and
generate. Also spends 1 credit per generation.
index.html — redirects to lamsa-auth.html.
The app supports 7 languages (Arabic, English, German, Spanish, Turkish, French, Italian, Russian) via a client-side translation dictionary; Arabic renders RTL.
api/)All endpoints are Vercel serverless functions (Node, export default async function handler(req, res)).
| Endpoint | Method | Purpose |
|---|---|---|
POST /api/auth-register | POST | Creates an account ({ username, email, password, country }), hashes the password (scrypt), starts a session, sets the lamsa_session cookie, grants the 1 free welcome credit. 409 if the email is already registered. |
POST /api/auth-login | POST | Verifies { email, password } against the stored hash, starts a session, sets the lamsa_session cookie. Returns a generic 401 for both "no such account" and "wrong password" (no user enumeration). |
POST /api/auth-logout | POST | Destroys the current session and clears the cookie. |
GET /api/me | GET | Returns { username, email } for the current session, or 401 if not logged in. Used by every protected page's auth guard. |
POST /api/upload | POST | Uploads a base64 image to fal.ai's CDN storage, returns a public url. |
POST /api/generate | POST | Requires a valid session. Deducts 1 credit from the logged-in user, then submits a { prompt, image_url, strength } job to fal.ai's flux-pro/kontext queue, returns a request_id. Refunds the credit if the fal.ai submission itself fails. Returns 401 if not logged in, 402 if the user has no credits left. |
GET /api/status | GET | Polls fal.ai's queue for a request_id (?mode=result fetches the final image once status is COMPLETED). |
GET /api/credits | GET | Returns { email, credits } for the current session. 401 if not logged in. |
POST /api/create-checkout-session | POST | Requires a valid session. Creates a Stripe Checkout Session (one-time payment) for { package, return_page }, returns { url } to redirect the browser to. 401 if not logged in. |
POST /api/stripe-webhook | POST |
Accounts are real and server-verified — not a localStorage fake.
crypto.scrypt (random per-user
salt, crypto.timingSafeEqual for comparison). Never stored in plaintext,
never sent back to the client.crypto.randomBytes(32)) is stored in
Upstash Redis as lamsa:session:<token> → email with a 30-day TTL, and
delivered to the browser as an httpOnly; SameSite=Lax cookie (Secure too,
outside of localhost). JavaScript on the page can never read the token./api/generate,
/api/credits, /api/create-checkout-session) reads the user's identity
only from that session cookie via requireSessionEmail() in api/_auth.js
— never from an email field in the request body or query string. This is
what actually stops one person from spending or charging another person's
credits by guessing/typing their email.lamsa-bilingual.html and rearrange.html call GET /api/me on load and redirect to lamsa-auth.html if it 401s. A cached
{username, email} in localStorage is used only to avoid a login-screen
flash before that check resolves — it has no authority on its own.Generation is metered with a simple one-time-purchase credit system — no
subscriptions. New emails get 1 free credit; each /api/generate call spends 1.
This is the beginning of the README. Read the rest at the original. Read the full README ↗
Verifies the Stripe signature and, on checkout.session.completed, credits the paying email's balance. Idempotent against duplicate webhook deliveries. |