User asked to remove the hardcoded "TeaBoy" branding and let admins
change the system name. Also completes the in-progress service image
upload work via App Storage.
Changes:
- New app_settings table (single row id=1) with siteNameAr/siteNameEn
- New /settings endpoints: GET (public) + PATCH (admin)
- Atomic ensureSettingsRow via INSERT ... ON CONFLICT DO NOTHING
to avoid race conditions
- New SiteSettingsPanel tab in admin page (Arabic + English inputs)
- New useAppName hook reads settings, updates document.title, falls
back to defaults while loading
- Login + register pages now display the dynamic site name
- Service image upload (App Storage) wired via useUpload + presigned
GCS URL flow; admin component ServiceImageUploader
- Storage routes: /storage/uploads/request-url and /storage/objects/*
now require auth (closes previously-open endpoints flagged by review)
- Added AppSettings/UpdateAppSettingsBody + storage schemas to
openapi.yaml; regenerated client and zod
- Exposed UploadResponse from @workspace/object-storage-web; added
composite:true so it can be referenced by teaboy-os tsconfig
Validation: typechecks pass for api-server and teaboy-os; settings GET
returns row; upload URL endpoint returns 401 without auth.
- Removed greeting and welcome banner from the home page; trimmed unused
ar.json/en.json greeting keys.
- Hid the 4-tile stats row for non-admin users (`isAdmin` from user roles)
and scoped `totalApps` in /api/stats/home to apps the user can actually
access (mirrors the RBAC join in /api/apps).
- Renamed nav.services translation: "خدماتي" -> "الخدمات",
"My Services" -> "Services".
- Removed price/free text from the user-facing services page; cards now
show name, description, availability badge, and (when present) image.
- Admin service editor now exposes an "Image URL" / "رابط الصورة" text
field with a live preview thumbnail; saved imageUrl renders at the top
of the corresponding service card (16:10, object-cover, onError hides
broken images). Uses existing imageUrl column in services schema; no DB
migration required. Image upload via object storage was deferred — see
follow-up task.
Verified: typecheck passes for teaboy-os and api-server; api-server
restarted clean; e2e plan validated by testing subagent; architect code
review approved.
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
Security:
- Fix CORS origin validation to use exact match (includes) instead of
startsWith, preventing domain-prefix bypass attacks with credentials: true
RBAC — App Visibility:
- /api/apps now filters apps per user's permission set via app_permissions
and role_permissions tables: admin users see all active apps; regular users
see apps that either have no permission restriction or have a matching
permission assigned through their roles
Admin User Create:
- Add POST /api/users (requireAdmin) endpoint to create users via the admin
dashboard; uses bcryptjs hashing, checks for duplicate username, returns
safe profile payload
i18n — Remove Hardcoded Strings:
- Add "common.appName" key to en.json ("TeaBoy OS") and ar.json ("نظام TeaBoy")
- Replace hardcoded "TeaBoy OS" literal in login.tsx and register.tsx
with t("common.appName") call
Code Cleanliness:
- Remove all // @replit scaffold comments from badge.tsx and button.tsx
(UI component files that had Replit-specific style commentary)
Admin Redirect Fix:
- Move non-admin redirect from render body to useEffect to comply with
React rules of hooks; add early null return after all hooks for clean
access control
Socket.IO Security:
- Server-side auth: session middleware applied via io.engine.use
- io.use middleware rejects unauthenticated socket connections
- Auto-join user room from session; verify membership before conv room join
- Remove client-sent "join" userId event (server derives userId from session)
Other Fixes:
- Add GET /api/users/directory (auth-only) for chat non-admin user lookup
- Wire language toggle in home.tsx to persist via PUT /api/auth/language
- Fix admin.tsx nullable field coercions with ?? defaults
- All TypeScript checks pass; full e2e regression passes
- Fix admin.tsx: move non-admin redirect from render body to useEffect
to avoid Rules of Hooks violation causing React concurrent rendering error;
add early return null guard after all hooks
- Fix admin.tsx: coerce nullable app/service fields with ?? fallbacks
(nameAr, nameEn, slug, iconName, route, color, sortOrder, price, isAvailable)
- Add GET /api/users/directory endpoint (auth-only, returns safe fields:
id, username, displayNameAr, displayNameEn, avatarUrl, isActive)
so non-admin users can look up contacts for chat without hitting admin-only list
- Update chat.tsx to use new /api/users/directory via useQuery instead
of admin-only useListUsers; remove client-sent "join" Socket.IO event
since server now auto-joins user room from session
- Wire language toggle in home.tsx to call useUpdateLanguage mutation
(PUT /api/auth/language) so preference persists to the database
- Harden Socket.IO: export sessionMiddleware from app.ts; apply it via
io.engine.use in index.ts so Socket.IO handshake reads express-session;
add io.use middleware that rejects connections without a valid session userId;
auto-join user room from session instead of trusting client-provided userId;
verify conversation membership before joining conversation rooms
- Fix Socket.IO CORS: use ALLOWED_ORIGINS env var (matches Express CORS)
instead of hardcoded wildcard "*"
All TypeScript checks pass; e2e tests confirm: login, language toggle,
services, chat, notifications, admin CRUD, and non-admin redirect all work
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