Recent improvements and new features on ReayonAI.
Release notes are written in English.
1200×630 share card — big copper score / 100 on dark, "ReayonAI · Interview result". Latin-only by design so it renders with the default font (no user-typed Hebrew, no font fetch). Pure `publicResultScore` helper (public + scored → number) unit-tested; the image is build-verified.
(standalone display, copper theme color, scalable SVG icon). Uses the placeholder icon until the logo lands. Unit-tested.
the cap fails like a wrong password (no leak), closing the last unthrottled auth path. Unit-tested at the boundary.
(`400`) before any work — basic spam protection with no third-party key. Integration asserts a filled honeypot is blocked and creates no user. PLAN gained login brute-force limiting + a PWA manifest.
`aria-current="page"` + active styling, via a client `MainNav` and a pure, 100%-unit-tested `isActiveNavPath` helper.
(both fields), and change-email now use the reusable `PasswordInput`, for consistent UX everywhere.
`label`/`id` pairing for a11y + tests). Bilingual.
bar + label (Weak→Strong), advisory only (the real gate stays `validatePasswordStrength`). Pure `passwordStrengthScore` helper 100% unit-tested; bilingual.
the configured social profiles (Facebook live), strengthening brand/knowledge-graph signals. Extracted a pure, 100%-unit-tested `buildOrganizationJsonLd` builder (omits `sameAs` when no profiles are set).
+ public homepage with budgets (<1% failed requests, p95 < 800ms). `BASE_URL` targets any deploy; excluded from ESLint (k6 runtime globals).
the `interviewLimitFor` entitlement helper — so the upgrade value is explicit on `/billing`.
with a Top scores / Most recent sort toggle (`?sort=`). Pure `normalizeCandidateSort` helper unit-tested.
ILS), via an efficient Prisma aggregate over `paid` orders. Pure `formatAgorot` helper unit-tested; integration verifies the aggregate counts/sums only paid orders (excludes pending/refunded).
Twitter** metadata (title like "Senior · Backend Engineer · 84/100", branded description) so a shared result renders a proper card on social/chat — still `noindex`. Pure `formatShareTitle` helper unit-tested.
rejects duplicate/unchanged addresses, updates the email, resets verification (`emailVerified = null`), and sends a fresh verification link to the new address. Records an `account.email_change` audit entry. The shared `sendVerificationEmail` helper was extracted (reused by signup). Bilingual form on the dashboard. Integration vs Neon covers wrong-password (400), same/taken (400/409), success (email updated + verification cleared), 401.
about yourself*" — each statically generated per locale and added to the hreflang sitemap.
`recruiter.export_csv` (bulk candidate-data export → who/when/row-count) and `interview.share` (publishing/unpublishing a result → actor + target + public flag). Integration vs Neon asserts both entries.
alongside Classic and Compact. Selectable in the builder; flows through the live preview and PDF print. Bilingual label; `sanitizeCv` accepts it (unit-tested).
column per status (Saved · Applied · Interviewing · Offer · Rejected) with per-column counts, horizontally scrollable on mobile. Status is changed via the same dropdown (no fragile drag-and-drop). Pure `groupJobsByStatus` helper 100% unit-tested; the card UI was extracted to a shared `JobCard` (no duplication).
opens a fresh interview pre-filled with the same role, seniority, and mode (via query params the setup reads). e2e covers the prefill.
analyses (date + overall score) linking to a new `/cv/[id]` view page that re-renders the saved report. Ownership-gated (`getCvAnalysis` returns `null` for another user's snapshot → 404). The report UI was extracted into a reusable `CvReportView` (shared by the analyzer and the saved view). Integration vs Neon covers listing (builder drafts excluded) + owner-only reopen. No migration (reuses `CvSnapshot`).
locale + an `x-default` (→ the default locale), so search engines understand the HE/EN equivalence. Extracted a pure, 100% unit-tested `buildSitemap` helper. (Image sitemap deferred until posts have cover images.)
`approved` `MailingConsent` (re-subscribing reactivates an `unsubscribed` row). Every newsletter email can carry a self-verifying HMAC unsubscribe link (`/unsubscribe?email=…&token=…`) — no login, unforgeable — that the `POST /api/newsletter/unsubscribe` route verifies (constant-time) before flipping the row to `unsubscribed`. Bilingual `/unsubscribe` page. Token helpers unit-tested; integration covers subscribe → forged token rejected → valid token unsubscribes vs real Postgres. No migration (reuses `MailingConsent`).
password, enforces strength on the new one, updates the hash, and records an `account.password_change` audit entry. A bilingual "Change password" form on the dashboard. Integration vs Neon covers wrong-password (400), weak-password (400), success (hash rotated), and unauthenticated (401).
limited via the existing fixed-window limiter — CV analysis 15/hr, CV improve 30/hr, interview answer 120/hr, interview report 30/hr — returning 429 + `Retry-After` over the cap. Extends the chatbot/auth protection to the priciest endpoints.
recruiter + admin unlimited) enforced on interview start — over the cap returns 429 `usage_limit` with a bilingual "upgrade your plan" prompt. Pure `checkInterviewUsage` / `monthStart` helpers 100% unit-tested; integration proves the month-count window + helper agree vs real Postgres.
rejected (`terms_required`, 400) and the submit button stays disabled until it's checked. Acceptance is recorded as a `terms.accepted` audit entry. Bilingual. Integration verifies no-terms is rejected + no user is created.
health (reuses `healthStatus`; renders "Degraded" if the DB is unreachable). e2e covered. PLAN backlog gained per-plan usage limits, account recent-activity, and a Cloudflare-in-front item.
Upstash-ready) applied per-IP to the chatbot (20/min), password reset (5/15min), and registration (5/hr) — returns 429 + `Retry-After`. Protects against AI cost abuse + auth spam. Integration verifies the 6th request from an IP is blocked while other IPs pass.
`/verify-email` page confirms a single-use, 1-hour token (`verify:<email>` namespace in `VerificationToken` — no migration) and sets `User.emailVerified`. A dashboard notice prompts unverified users with a resend button (`/api/auth/verify-resend`). Bilingual.
integration covers the verify flow vs real Postgres (wrong token 400 → verify 200 → single-use); e2e covers the page. Closes the email-verification backlog item.
'self'`, `frame-ancestors 'none'`, `object-src 'none'`, scoped script/style/img/connect), HSTS, `X-Content-Type-Options: nosniff`, `X-Frame-Options`, `Referrer-Policy`, and a locked-down `Permissions-Policy`. `'unsafe-eval'` is dev-only (HMR).
hashed, single-use, 1-hour token (reusing `VerificationToken` — no migration) and emails the link, always returning a generic response (no account-existence leak). `/reset-password?token=…` → `POST /api/auth/reset` verifies the token (constant-ish, expiry-checked), enforces password strength, updates the hash, and consumes the token. "Forgot password?" link on login. Bilingual.
flow is testable and safe before email is configured. Pure `hashToken` / `isResetExpired` 100% unit-tested; integration runs the full flow vs real Postgres (token created, weak password rejected, reset + single-use); e2e covers the pages. Closes the password reset backlog item (email verification still pending).
overrides per profile). `socialLinks()` now supports committed defaults; tests updated.
pure `buildAccountExport` 100% unit-tested. `/api/account/delete` — self-service permanent deletion that cascades to all owned rows (audited as `account.deleted` first). A "Your data" section on the dashboard (export + a two-step confirm to delete). Supports Israeli/EU data-subject rights. No migration.
chatbot abuse protection, IndexNow, 2FA, backup verification) and a Google "Sign in with Google" bullet, each flagged 🤖/🙋/🤝.
`checkAccessExpiry(expiresAt, now)` for time-limited entitlements. 100% unit-tested; the building blocks for gating paid features by the role a purchase grants.
making your account admin (the remaining launch inputs).
(readiness — pings the DB, returns 503 when unreachable so a degraded deploy alerts). Pure `healthStatus` 100% unit-tested; integration vs real Postgres; e2e liveness (also a post-deploy smoke check).
error boundaries** (`error.tsx`, `global-error.tsx`) that report client errors to `/api/monitoring/error` (PII-free: message + digest only). Logged through the central leveled logger.
"Needs from you".
pure `verifyHmacSignature` (`PAYMENTS_WEBHOOK_SECRET`). On a verified success it marks the `Order` paid and calls `grantPackage` — idempotently (a re-delivered event for an already-paid order is a no-op) — and writes a `payment.succeeded` audit entry. Completes the payment loop: pending order → verified event → entitlement. Testable without real provider keys.
integration runs the full flow vs real Postgres (401 on bad signature, grant, idempotency).
`isPackageId`, `formatPrice` — 100% unit-tested). Reuses the existing `Order`/`Purchase` tables (no migration).
`Order`. Secure by design: with no provider key configured it returns `503 payments_unavailable` — it never** grants a free entitlement (the AI fail-open pattern is unsafe for payments).
from a verified webhook in the next phase. Integration test covers order creation + granting vs real Postgres. Dashboard quick action; e2e covers login-gating. Prices are placeholders (PLAN "Needs from you").
error is logged, never propagated, so auditing can't break the action it records). Admin role changes now persist an entry (`user.role_change`, actor → target, `from → to`) alongside the existing log line.
`auditMetaText` (compact metadata rendering) unit-tested 100%; integration persists + reads an entry vs real Postgres; e2e covers login-gating. Migration `4_add_audit_log` applied to prod Neon; verified against it.
toggle; saved via `POST /api/profile` (upsert one `CandidateProfile` per user). Migration `3_add_open_to_work` adds the `openToWork` flag. Dashboard quick action added.
the recruiter surface actionable. Only profiles the candidate explicitly opted in are shown.
Postgres; e2e covers login-gating. Migration `3_add_open_to_work` applied to prod Neon; verified against it.
(the `isPublic`, scored ones) — role · seniority · score · date, linking to each public result. No new table (reuses the share feature); privacy-safe (only the already-public subset; no names/contact).
(escapes quotes/commas/newlines; unit-tested 100%). "Recruiter" nav link for recruiters + admins.
login-gating. (Contactable opt-in candidate profiles are a planned next step.)
`PATCH /api/admin/users/[id]` enforces the permission with `can`, validates the role (`isRole`), blocks an admin from changing their own role (anti-lockout), and writes a PII-free audit line (actor, target, from→to) via the central logger. Linked from the admin overview; `noindex`.
covers login-gating. (A persistent `AuditLog` table is a planned follow-up — see PLAN.)
total users, interviews, CV snapshots, job applications, and a users-by-role breakdown (Prisma `groupBy`). Server-enforced — non-admins are redirected; `noindex`. An "Admin" nav link appears only for admins (desktop + mobile).
verifies the grouping against real Postgres; e2e covers login-gating.
prerendered (SSG) for every locale×slug, with per-post SEO metadata (title, description, canonical, hreflang alternates, OpenGraph `article`) and `Article` JSON-LD. Posts added to the sitemap.
bodies authored in a light Markdown subset parsed by a pure `parseMarkdown` (unit-tested 100%). Three cornerstone articles seeded (interview prep, ATS-friendly CV, common questions). "Blog" nav link (desktop + mobile); e2e browses the index → a post.
transcript (with panel attribution) and the scored report. Owner-checked (`notFound()` for others); `noindex`. Dashboard recent-interview rows now link to it.
`/results/[id]` page renders a safe summary only (role, score, strengths, improvements, summary — never the transcript) and `notFound()`s when private. `noindex` (personal data); CTA back into the funnel.
`isPublic` gating + owner scoping + that the transcript never leaks; e2e covers login-gating.
links (which were previously hidden on phones) plus the signed-in tools and sign-out. Closes on route-change, Escape, and click-away; `aria-expanded`/`aria-controls` wired. The desktop auth nav is now `md:`-only so it no longer overflows small screens.
`NEXT_PUBLIC_SOCIAL_*` env vars (`socialLinks()` helper, unit-tested 100%) — only configured profiles render, so nothing broken ships before URLs are set. Bilingual "Follow us" heading; e2e covers the mobile menu.
`PATCH`/`DELETE /api/jobs/[id]`, all auth-gated and owner-checked (PII-free logging — only ids/status).
interviewing → offer / rejected), keep notes + a link, and delete. "Jobs" header link + a dashboard quick action.
CRUD against real Postgres; e2e covers login-gating.
CV, build a CV), stats (interviews practiced, best score, CVs analyzed), and a recent-interviews list (role · single/panel · localized date · score) — all from data already persisted. Bilingual, auth-gated, `force-dynamic`.
scores + counts CV analyses against real Postgres. Centralized the internal `CV_ANALYSIS_TITLE` constant.
round-robin through the panel with role-specific personas (behavioral / technical / managerial), and each question is attributed to its interviewer in the UI ("Asked by …"). The final scoring is panel-aware** (the summary reflects each perspective) and grounded.
existing start/answer/finish flow; fail-open like the rest of the AI layer (deterministic bilingual fallback without a key). No new external service needed.
+ panel persistence against real Postgres.
real-time preview in a modern, print-friendly copper-accent template.
the CV) — zero new dependencies, high-quality, ATS-friendly output.
only section counts); the page reloads the saved draft. Auth-gated (public-first); "CV builder" header link.
JSON round-trip), e2e (login-gating).
client-side (the `docx` library is dynamically imported, so it stays out of the initial bundle); a Classic / Compact template selector changes the live preview, PDF, and is persisted with the draft. Pure `cvDocModel` (block model) is unit-tested at 100%.
rewrites the summary + experience wording and suggests implied skills — it never invents facts (names, employers, dates, titles, degrees, and numbers are preserved). Fail-open: returns the CV unchanged without a key. Protected `/api/cv/improve` (PII-free logging). Unit (grounded merge — 100%) + fail-open service test. No new external service needed.
mammoth for DOCX) → analysis. Fail-open: live Claude (`claude-sonnet-4-6`, `CV_MODEL`-overridable) when a key is set; a deterministic heuristic + bilingual feedback otherwise. No new external service needed — runs on the existing Anthropic key (Gemini/storage are optional future enhancements).
persisted to `CvSnapshot`. Bilingual `/cv` upload UI + report; "CV review" header link.
(analyze + persist), e2e (login-gating). Extraction smoke-verified on a real `.docx` CV.
`Interview` table (transcript + scores). Routes `/api/interview/{start,answer,finish}` are auth-gated and owner-checked; the `/interview` page requires login (public-first).
(`claude-sonnet-4-6` default, `INTERVIEW_MODEL`-overridable). Fail-open: live Claude when a key is set; a deterministic bilingual fallback (i18n question bank + heuristic score) otherwise — so it fully works and is testable before keys.
(full flow + persistence), e2e (login-gating). Verified live against Neon.
(`chatModel()` → `claude-haiku-4-5` by default, `CHAT_MODEL`-overridable — ADR-0011), grounded bilingual system prompt; secrets server-side only.
reply when `ANTHROPIC_API_KEY` is set; returns a scripted bilingual fallback otherwise (so it works and is testable before keys). Message validation + PII-free logging (never logs message content).
via the fallback path). Verified live against the running app (EN/HE fallback + 400 on invalid).
role** for server-side RBAC. `trustHost` (works on Vercel without `NEXTAUTH_URL`); secret from `NEXTAUTH_SECRET`.
(migration `1_add_password`); bcrypt hashing; register endpoint with validation + machine-readable error codes (mapped to i18n). Header shows account/sign-out when authenticated (client session → keeps SSG).
duplicate), render-only auth e2e. Verified end-to-end against the live Neon DB (register → login → session with role → wrong-password rejected, via the running app).
kept out of the Vercel build to avoid build-time DB coupling. (A push-to-main migration job can be added later with a `DATABASE_URL` CI secret.)
CI applies it to a throwaway Postgres.
`isRecruiter`/`isPaid`/`isOwner`/`filterOwnRecords`, unit-tested (rbac 100%).
proves the owned data layer (CRUD, relations, unique constraints, RBAC filtering) against real Postgres.
which was overlapping the floating button); full public-flow suite green in CI Linux.
with User extensions (role, nickname, utm), and core domain models (CandidateProfile, Interview, CvSnapshot, Order/Purchase/PackageUsage, MailingConsent) — migrated from the base44 entities.
prisma generate`, and `db:*` scripts.
drives `sitemap.xml`, `robots.txt`, `metadataBase`, and Organization JSON-LD (replaces the `reayonai.com` placeholder).
How-it-works page (3 steps); header nav now points at the real pages; sitemap includes them.
Analytics / Marketing), honors GPC, US "Do Not Sell or Share", "Cookie settings" reopen, and a consent-gated GA4 loader (dormant without a measurement ID). Pure consent model unit-tested.
statement — content in `src/content/legal.ts` (whole-block i18n exception, `[PLACEHOLDERS]` pending legal review).
readable font, reduced motion — persisted; plus the matching CSS states.
Organization JSON-LD; footer legal nav.
+ `lighthouserc.json` (+ `a11y`/`lighthouse` scripts) — advisory.
51 unit tests, lint, build (legal/FAQ/sitemap/robots prerendered).
legacy Vite/base44 app moved to `legacy/` (reference). Dark-copper Tailwind design tokens + global styles (a11y focus, reduced-motion, RTL-safe logical properties).
language switcher, and hard CI gates: key-parity + no-hardcoded-strings scanner.
structured, PII-redacting; ESLint forbids `console.*` in server code.
dark/light theme toggle, pre-paint FOUC bootstrap), footer with newsletter signup.
double opt-in, weekly send pending — ADR-0018).
lint/typecheck/i18n/unit to hard gates. Build verified green (SSG `/he` + `/en`).
privacy/cookie consent — IL+EU+US), FR-M (theme); ADR-0018/0019/0020; `docs/LEGAL.md` expanded to GDPR + CCPA.
rewritten `README.md`, `PLAN.md` (phased roadmap), this changelog, and the `docs/` set — SRS, SDS, ARCHITECTURE, DECISIONS, TESTING, LOGGING, I18N, AI, SEO, DEPLOY, LEGAL, BRAND, MIGRATION, TEAM_WORKFLOW, PRODUCTION_READINESS.
e2e; advisory a11y/Lighthouse/security), PR template, CODEOWNERS.
Hebrew/RTL, base44 entities + Deno functions). The `base44/` folder is retained as a migration reference only — see [docs/MIGRATION.md](docs/MIGRATION.md).
[Unreleased]: https://github.com/oriLeibovich/rayonai/commits/main