psd2live
레이어가 있는 PSD를 편집 가능한 Live2D 모델로 변환
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
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.
api/_stripe.js → CREDIT_PACKAGES): 1 credit / €1, 5 credits / €4,
10 credits / €7.api/_db.js), not localStorage,
so they can't be reset by clearing browser storage.payment_method_types left
unspecified, so it auto-includes whatever the Stripe Dashboard has enabled for
the account's country — typically cards plus Apple Pay / Google Pay. Stripe
does not natively support GCC-local rails (mada, STC Pay); a supplementary
PSP (Moyasar, Tap, HyperPay, PayTabs) would be needed for those specifically.STRIPE_SECRET_KEY.https://<your-domain>/api/stripe-webhook, subscribed to
checkout.session.completed. Copy its signing secret into
STRIPE_WEBHOOK_SECRET.KV_REST_API_URL / KV_REST_API_TOKEN (or
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN if provisioned directly
via Upstash) — api/_db.js reads either naming.| Variable | Required by | Description |
|---|---|---|
FAL_API_KEY | api/upload.js, api/generate.js, api/status.js | fal.ai API key used for image generation and storage. |
ANTHROPIC_API_KEY | (currently unused by any endpoint) | Reserved for Claude-vision features. |
STRIPE_SECRET_KEY | api/create-checkout-session.js, api/stripe-webhook.js | Stripe secret key (test or live). |
STRIPE_WEBHOOK_SECRET | api/stripe-webhook.js | Signing secret for the checkout.session.completed webhook endpoint. |
KV_REST_API_URL / UPSTASH_REDIS_REST_URL | api/_db.js (credits, webhook idempotency) | Upstash Redis REST URL — auto-injected by the Vercel Marketplace Upstash integration. |
KV_REST_API_TOKEN / UPSTASH_REDIS_REST_TOKEN | api/_db.js | Upstash Redis REST token — auto-injected alongside the URL above. |
Set these in your Vercel project settings, or in a local .env file when running
with vercel dev.
This is a static site with serverless API routes, deployed on Vercel. There's no
build step for the frontend, but the API functions now depend on npm packages
(stripe, @upstash/redis), so install them once:
npm install
npm i -g vercel
vercel dev
This serves the HTML pages and proxies /api/* to the functions in api/, so the
upload/generate/status calls work locally exactly as they do in production.
Opening the .html files directly (e.g. via file:// or a plain static server)
works for UI/layout iteration, but any /api/* call will fail without vercel dev
or a deployed backend.
The project deploys to Vercel as-is. vercel.json routes /api/* to the
serverless functions, serves any *.html path directly, and redirects / to
lamsa-auth.html.
├── index.html # redirects to lamsa-auth.html
├── lamsa-auth.html # login / register
├── lamsa-bilingual.html # main designer (upload, floor plan, style, generate)
├── rearrange.html # quick rearrange / full redesign from one photo
├── assets/
│ ├── favicon-32.png / favicon-16.png / apple-touch-icon.png # browser tab / home-screen icon
│ └── logo-mark.png # icon-only mark (transparent), used in the nav logo
├── data/
│ └── furniture-stores.json # store names by country (Gulf + Europe)
├── js/
│ └── nearby-stores.js # shared "shop similar pieces" render logic
├── api/
│ ├── auth-register.js # create account, hash password, start session
│ ├── auth-login.js # verify password, start session
│ ├── auth-logout.js # destroy session
│ ├── me.js # who's logged in, from the session cookie
│ ├── upload.js # image upload → fal.ai storage
│ ├── generate.js # deduct a credit, submit a generation job to fal.ai
│ ├── status.js # poll job status / fetch result
│ ├── credits.js # get the logged-in user's credit balance
│ ├── create-checkout-session.js # start a Stripe Checkout session
│ ├── stripe-webhook.js # grant credits once Stripe confirms payment
│ ├── _auth.js # password hashing + session helpers (not a route)
│ ├── _db.js # Upstash Redis client + credit helpers (not a route)
│ └── _stripe.js # Stripe client + credit package pricing (not a route)
├── package.json
└── vercel.json
See TODO.md for the fuller list of planned hardening work.
Verifies the Stripe signature and, on checkout.session.completed, credits the paying email's balance. Idempotent against duplicate webhook deliveries. |