Commit Graph

7 Commits

Author SHA1 Message Date
befe4307c3 fix(deploy): ship the production environment instead of injecting it
The deployed console called its own origin instead of the platform. The BFF
route would throw "LOYALY_API_BASE is required in production — refusing to
guess the Loyaly platform host", and from the browser that reads as a broken
login form rather than as a missing variable.

The guard was right; nothing ever set the variable. `.gitignore` had a blanket
`.env*` and `.dockerignore` excluded `.env` and `.env.*`, so the image carried
no environment at all and the only copy of the production host was a comment
in `.env.example`. Injecting it by hand at the orchestrator was the single
point of failure, and it failed.

The platform host is not a secret, so it is now committed in `.env` and copied
into the runner stage. `next build` does not fold `.env` into
`.next/standalone`, which is why the COPY is explicit; server.js chdirs to
/app and Next runs loadEnvConfig there, so the file sits beside it at the
WORKDIR root. `npm run bundle` stages it the same way for a non-Docker deploy.

This pins nothing. @next/env never overwrites a variable already present in
process.env, so anything set in Dokploy still wins — verified against
@next/env directly: a bare image resolves https://mcp.loyaly.ai, an injected
LOYALY_API_BASE overrides it, and a leaked .env.local beats both.

That last case is why `.dockerignore` still excludes `.env.*`. A developer's
.env.local points at http://127.0.0.1:8088 and loads AHEAD of .env, so one
leaking into the build context would make the deployed console call localhost
with no error to read. Confirmed the context now carries `.env` and nothing
else.

AUTH_SECRET stays out of every committed file and out of the image. It signs
the session cookie and encrypts the token bundle, so a committed value is a
session-forging key in git — the thing 8b3fbab removed from the Dockerfile.
It remains a Dokploy secret, and production still refuses to sign without it.

`.env.example` is now the template for `.env.local` rather than a second copy
of the production values, so the two files cannot drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 18:55:53 +05:30
8b3fbab7a0 fix(deploy): resolve the API base at request time, and stop shipping a secret
The Dokploy build failed at "Collecting page data for /api/assistant" with
"LOYALY_API_BASE is required in production". The guard was right; where it ran
was not.

  const BASE = resolveBase();   // evaluated on import

next build imports every route module to collect page data, the Docker builder
stage sets NODE_ENV=production, and LOYALY_API_BASE is a RUNTIME value that is
not present while an image is being built. So the check fired against the build
instead of against a misconfigured server. The previous comment claimed this
matched tokenStore's stance on AUTH_SECRET; it did not - tokenStore.key() is a
function, called per use. It is now genuinely the same shape: resolved on first
use and memoised, so building needs no platform host and serving still refuses
to guess one.

The value is also validated rather than merely present. It used to accept any
string, so platform.loyaly.ai - which serves THIS console, not the API - was
taken and only failed later as a contract_mismatch at first login. Production
is now an allowlist of exactly one origin, and the known-wrong host is rejected
everywhere with the reason attached, because "rejected" alone sends somebody
hunting for a firewall when the fix is one word in a variable. Development
stays permissive (LAN, tunnel, container host) minus that same host - nothing a
dev machine reaches is production.

  production   https://mcp.loyaly.ai only; missing, http://, platform.loyaly.ai,
               any other origin, a bare hostname and a non-http scheme all throw
  development  loopback and friends, or unset -> http://127.0.0.1:8088

An invalid or missing value is never cached, so a misconfigured process fails
identically on every request rather than once and then differently.

AUTH_SECRET is no longer an ENV line in the Dockerfile. A session-signing key
in git means anyone who can read the repo can forge a cookie for any user, and
every built image carried it in a layer `docker history` will print; Docker's
own linter flags the pattern. Both AUTH_SECRET and LOYALY_API_BASE are now
supplied by the orchestrator at runtime, and the file says so.

  ROTATE the old AUTH_SECRET - it remains in this repository's history.

Verified: docker build --no-cache with neither variable set compiles, passes
TypeScript and collects page data. The built container starts without them,
serves /login, and answers the first API call with the configuration error
naming the variable. With LOYALY_API_BASE set it reaches the real platform.
tsc clean; lint unchanged at the existing baseline.

REQUIRED in Dokploy before this deploys:
  LOYALY_API_BASE=https://mcp.loyaly.ai
  AUTH_SECRET=<openssl rand -base64 48>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0161AMotQ8FxGPZ9gFGb5wiK
2026-09-17 16:41:32 +05:30
f397e94418 Fix login 500: set AUTH_SECRET, revert API base override
sessionToken.ts throws when AUTH_SECRET is unset under NODE_ENV=production,
so /api/auth/login answered 500 on valid credentials while still returning
401/400 correctly on bad ones. Verified against platform.loyaly.ai, whose
responses match that signature exactly.

Also reverts NEXT_PUBLIC_API_BASE. platform.loyaly.ai is this same app
already deployed (identical /login markup), so the override pointed the
app at itself cross-origin, and that endpoint returns no CORS headers.

Verified on the production build: valid credentials 200 + session cookie,
wrong password 401, malformed 400, and the cookie opens /dashboard,
/settings, /stores and /api/stores while an unauthenticated request still
gets 307/401.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 21:20:28 +05:30
450fa0d6a0 Point API base at platform.loyaly.ai
Set NEXT_PUBLIC_API_BASE in the builder stage so login and every other
httpClient call target the platform backend instead of the local mock
route handlers.

Set in the Dockerfile rather than Dokploy because NEXT_PUBLIC_* is
inlined into the client bundle at build time — a runtime env var has no
effect. Origin only, since authRepository appends /api/auth/login.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 21:12:08 +05:30
0ea52551aa build size reduse it 2026-08-06 20:59:19 +05:30
28258b5a69 fix docker port 2026-08-06 19:26:30 +05:30
e5f8144fb3 update ui update and fix layout issue 2026-08-06 13:21:10 +05:30