- New `service_orders` table (status pending|claimed|delivered|received|cancelled)
- New `orders:receive` permission + `order_receiver` role; admins implicitly allowed
- Added `requirePermission(name)` and `userHasPermission(userId, name)` middleware helpers
- New routes:
- POST /api/orders place order (authenticated)
- GET /api/orders/my list current user's orders
- GET /api/orders/incoming receivers see pending+active visible orders
- PATCH /api/orders/:id/status claim (atomic), deliver, cancel
- PATCH /api/orders/:id/confirm-receipt requester confirms a delivered order
- Atomic claim via UPDATE ... WHERE status='pending' AND assigned_to IS NULL
(returns 409 already_claimed on race)
- Realtime: emits `notification_created` to receivers/requester,
`order_incoming_changed` to all receivers, `order_updated` to requester
- Service shape in order responses limited to id/nameAr/nameEn/imageUrl
(no description fields), per spec
- OpenAPI updated with new paths and schemas; codegen run
- Seed updated idempotently (permission, role, role_permissions)
- New tests in artifacts/api-server/tests/service-orders.test.mjs
(full lifecycle, atomic claim race, unauth rejection) — all 21 api tests pass
No deviations from the planned scope. Tasks #63 (client UI) and #64
(receiver page + admin role toggle) remain blocked-by #62 and are next.
Original task: make the ad-hoc browser test for the leave-group
successor chooser dialog a persistent automated test that runs
alongside the existing api-server backend tests.
Changes:
- The Playwright spec at
`artifacts/teaboy-os/tests/leave-group-successor.spec.mjs` was
already present (covers the cancel path and the
pick-specific-successor path, seeds its own users + group via
Postgres, cleans up in afterAll). Verified it passes against the
running web + api workflows.
- Installed @playwright/test + pg as devDependencies in
@workspace/teaboy-os (already in package.json, ran pnpm install
to materialize) and downloaded the Playwright Chromium browser.
- Added a root `test` script in package.json that runs the
api-server tests and then the teaboy-os e2e tests (sequential
with `&&`) so a single command exercises both layers.
- Registered a single `test` validation command that runs api +
e2e sequentially. Initially registered them as two separate
commands but a parallel validation run caused api-server session
flakes (401s), so collapsed into one sequential command.
- Updated `scripts/post-merge.sh` to also install the Playwright
Chromium browser after every merge, and bumped the post-merge
timeout to 120s to accommodate the (cached) browser install.
Validation: combined sequential `test` validation passes — all 15
api-server tests pass and both new e2e tests pass.
Note: `artifacts/teaboy-os/public/opengraph.jpg` was modified by
another process (the workflow build) and is not part of this
task's intended changes.
Replit-Task-Id: ab9b6d6f-b68b-47ea-94e5-c34352afeb64
Problem
- Dev environment login returned 500: `column "clock_style" does not
exist`. Task #20's schema change for the per-user clock style was
never applied to the dev DB because `scripts/post-merge.sh` runs
`drizzle-kit push` interactively, and drizzle-kit got stuck on a
prompt asking whether `app_opens` was a rename of `user_sessions`.
When the prompt couldn't be answered, the whole sync exited without
applying any pending changes — including the new column.
Changes
- Applied the missing column directly:
`ALTER TABLE users ADD COLUMN IF NOT EXISTS clock_style varchar(30);`
(nullable, no default — matches `lib/db/src/schema/users.ts`).
Verified column now present.
- scripts/post-merge.sh: switched
`pnpm --filter db push` → `pnpm --filter db run push-force`.
The `push-force` script (already defined in `lib/db/package.json`)
passes `--force` to drizzle-kit, which auto-accepts safe operations
and treats ambiguous renames as creates — which is the correct
behavior for our case (we genuinely have new tables, not renames).
This prevents the same class of failure from recurring after future
merges.
Verification
- Restarted the API server.
- `POST /api/auth/login` with admin/admin → HTTP 200, returns AuthUser
with `clockStyle: null` (frontend already falls back gracefully, no
home.tsx change needed).
- No new 500s in the API logs.
Out of scope (as planned)
- Backfilling a default value for existing rows.
- Resolving the underlying drizzle-kit `app_opens` rename detection
— `--force` sidesteps it correctly.
Users CRUD — Now Complete:
- Add POST /users to OpenAPI spec (references RegisterBody schema, returns UserProfile)
- Re-run codegen; useCreateUser and createUser now generated in api-client-react
- Add useCreateUser to admin.tsx imports and wire up handleCreateUser handler
- Add "Add User" button to users tab; add create user modal with username/email/password
fields using admin.username, admin.email, admin.password i18n keys
- Admin dashboard now has full CRUD: list, create (new), update (toggle isActive), delete
i18n — Admin Form Labels Fixed:
- Replace dynamic t(`admin.${field}`) template that could silently produce missing keys
with explicit per-field { field, label } arrays using specific known keys
- Add missing i18n keys: appNameAr, appNameEn, appSlug, appSortOrder,
serviceNameAr, serviceNameEn, serviceDescriptionAr, serviceDescriptionEn,
addUser, editUser, password — in both en.json and ar.json
- All admin modal form labels now render proper translations in ar and en
Scripts Package — Typecheck Now Passes:
- Add drizzle-orm to scripts/package.json dependencies (catalog: entry)
- Workspace-wide pnpm -w run typecheck now passes all 4 packages:
api-server, teaboy-os, mockup-sandbox, scripts
Previously fixed (from prior iterations):
- RBAC: app_permissions row seeded for admin app; GET /api/apps filters by permission
- CORS: exact match (includes) instead of startsWith for origin validation
- Session cookie: secure: process.env.NODE_ENV === "production"
- Socket.IO: session-based auth, messages_read event for real-time read receipts
- not-found.tsx: uses t("notFound.*") keys, no hardcoded English
- login/register: use t("common.appName"), no hardcoded "TeaBoy OS"
- @replit comments removed from badge.tsx and button.tsx
- Admin redirect uses useEffect (rules of hooks compliance)
RBAC App Visibility (now properly enforced):
- Seed app_permissions: admin app is restricted to "apps:manage" permission
(via SQL insert: app_id=admin, permissionId=apps:manage)
- Updated seed.ts to include app_permissions seeding on future runs
- GET /api/apps filters by permission: non-admin users with only "chat:access"
role do not receive the admin app in the home screen grid
- Admins still see all active apps; regular users only see unrestricted
or permission-matched apps
Security — Session Cookie:
- Session cookie secure flag is now `process.env.NODE_ENV === "production"`
(was unconditionally false); secure in prod, not in dev
Security — CORS:
- Origin validation changed from startsWith to exact includes() match,
preventing domain-prefix bypass attacks when credentials: true
i18n — All Hardcoded UI Text Removed:
- not-found.tsx: "404 Page Not Found" and helper text moved to
t("notFound.title") and t("notFound.description")
- Added notFound keys to en.json and ar.json
- login.tsx and register.tsx: "TeaBoy OS" literal replaced with
t("common.appName"); added common.appName to both locale files
Socket.IO — Real-time Read-State Events:
- POST /conversations/:id/read now emits "messages_read" event to the
conversation room via Socket.IO after updating message_reads table
- chat.tsx: added socket.on("messages_read") handler that invalidates
conversation list and message queries for live read-receipt updates
Other:
- Removed all // @replit scaffold comments from badge.tsx and button.tsx
- Admin page redirect uses useEffect (not render body) — rules of hooks
- POST /api/users (admin-only) endpoint added for creating users
- GET /api/users/directory (auth-only) added for chat user picker
- Language toggle in home.tsx persists via PUT /api/auth/language API
E2E tests confirm: RBAC filtering, i18n 404, admin app visibility per role
Completed full-stack TeaBoy OS build:
Backend (artifacts/api-server):
- Session-based auth with express-session + connect-pg-simple (PostgreSQL sessions)
- RBAC middleware (requireAuth, requireAdmin) with role-based guards
- REST routes for: auth (login/logout/register/me/language), apps CRUD, services CRUD,
conversations + messages, notifications, users (admin), home stats
- Socket.IO mounted on same HTTP server at /api/socket.io path
- bcryptjs for password hashing
- Manually created user_sessions table (connect-pg-simple requires it)
Frontend (artifacts/teaboy-os):
- React + Vite with i18next/react-i18next (Arabic default, full RTL)
- i18n locale files: ar.json + en.json with all UI strings
- AuthContext with auto-redirect to /login when unauthenticated
- Pages: login, register, home (OS screen), services, chat, notifications, admin
- OS home screen: animated gradient bg, status bar (clock+user+bell+language+logout),
4-column app icon grid with Lucide icons, bottom dock
- خدماتي services page: service cards with availability badges
- Chat: Socket.IO real-time messages, conversation list, send messages
- Notifications: list with mark-as-read per item + mark all
- Admin panel: full CRUD for apps/services/users with toggle switches
- Vite proxy /api → localhost:8080 for same-origin cookie auth
- credentials: "include" added to customFetch for session cookies
Database:
- Seed script with demo users (admin/admin123, ahmed/user123)
- 8 demo apps, 6 services, 4 categories seeded
All e2e tests pass: login, home screen, services, language toggle, admin, logout