# CODEX.md This file provides guidance to Codex when working with code in this repository. ## Project Overview Allapp is a family/space mini-program platform — a main shell with sub-apps inside it. The frontend owns most business logic; the backend is deliberately thin and generic (auth, file storage, ID generation, database access). - **Backend**: `app-go/` — Go 1.25, Fiber v3.1, pgx (PostgreSQL), JWT, Viper, Zap - **Frontend**: `app-rn/` — React Native 0.85, Expo 56, Expo Router, Zustand, Alova, HeroUI Native, Uniwind (Tailwind CSS) - **Database**: PostgreSQL 18.1, database `allapp`, schema `public` Read `PROJECT_CONTEXT.md` for deeper context; this file focuses on what you need day-to-day. ## Commands ### Backend (`app-go/`) ```bash cd app-go go build ./cmd/app # Build go run ./cmd/app # Run server go test ./... # Run all tests go mod tidy # Tidy dependencies ``` ### Frontend (`app-rn/`) ```bash cd app-rn pnpm start # Expo dev server (port 8083) pnpm android # Android build pnpm ios # iOS build pnpm lint # oxlint pnpm exec tsc --noEmit # TypeScript type check pnpm exec oxfmt # Format code ``` WSL-specific: if `node`/`pnpm` are not found, run: ```bash export PATH="$HOME/.local/share/fnm:$PATH" eval "$(fnm env --shell bash)" ``` ## Architecture ### Bootstrap Flow ``` Backend: config → logger → S3 → Postgres → db.Init → unique ID → JWT → WeChat → Fiber server Frontend: /index → token check → auth/login or family/entry or /home ``` ### Backend Design **All CRUD goes through 5 generic data APIs.** Never create dedicated REST endpoints for feature-specific operations: | Endpoint | Purpose | |---|---| | `POST /app/data/loadData` | Query by table/view name, conditions, columns, order, args | | `POST /app/data/loadDataPage` | Paginated query — same as loadData + `page`/`page_size`, returns `{rows, total, page, page_size}` | | `POST /app/data/loadDataBySql` | Raw SQL query | | `POST /app/data/saveData` | Batch insert/update/delete by table name and key field | | `POST /app/data/getUniqueId` | Get snowflake IDs (1–100) | Auth endpoints: `/app/auth/login/{qq,wechat,test}`, `/app/bind/{qq,wechat}`. **File endpoints:** | Endpoint | Purpose | |---|---| | `POST /app/s3/upload` | Upload files (multipart form, field name `files`), stored under `YYYY/MM/DD/{uuid}.ext`, returns `string[]` keys | | `POST /app/s3/delete` | Delete files by keys, body `{keys: string[]}`, failure is logged but still returns success | On the frontend: `s3UploadFileApi(files[])` wraps files into a `FormData` with key `"files"` and `multipart/form-data` content type. For deleting uploaded files, use `deleteS3FileQuietly()` from `app-rn/src/utils/file.ts`; do not reimplement S3 key checks or silent delete/catch logic in pages or components. Auth token is passed in a custom `Token` header. JWT middleware stores `user_id` in context. Audit fields (`create_time`, `update_time`, `create_by`, `update_by`) are auto-injected by `pkg/db/meta_field.go` for all tables except `b_user` and `b_user_oauth`. ### Frontend Design Expo Router file-based routing under `src/app/`: - `(main)/(tabs)/` — main shell tabs (home, chat, my) - `(main)/auth/`, `(main)/family/`, `(main)/tools/`, `(main)/activities/` - `finance/(tabs)/` — finance sub-app (independent from main) - Layouts: `AppLayout` for main shell, `ModuleLayout` for sub-app shells, `PageLayout` for simple pages State: Zustand with `persist` middleware for `user` and `family` (AsyncStorage). Finance store is in-memory only. API layer: single Alova instance (`src/request/index.ts`), API wrappers in `src/request/api.ts`. Key wrappers: `loadDataApi`, `loadDataPageApi` (paginated, returns `PageData` with `rows`/`total`/`page`/`page_size`), `loadDataBySqlApi`, `saveDataApi`, `getUniqueIdApi`. Response codes: 1000 (success), 1001 (unauthorized → redirect to login), 2000 (business error), 3000 (system error). Startup flow in `src/app/index.tsx`: ``` no token → /auth/login token invalid → /auth/login no family → /family/entry has family → /home ``` ## Critical Conventions ### 1. No New Dedicated API Endpoints Always reuse the 5 generic data APIs. If you think a new endpoint is needed, **ask the user first**. Even then, new endpoints must be generic helpers, not feature-specific. ### 2. Database Tables Must Include Audit Columns All new business tables must include: `id BIGINT PRIMARY KEY` (snowflake), `create_time TIMESTAMPTZ`, `update_time TIMESTAMPTZ`, `create_by BIGINT`, `update_by BIGINT`. Do not add new tables to `auditExcludeTables` — only `b_user` and `b_user_oauth` are exempt. ### 3. Family/Space-Centric Data Model All business data (finance, favorites, etc.) associates with `family_id`. Users link to families via `b_family_member`. Always filter queries by the current user's `family_id`. ### 4. Frontend Family-Scoped Data Access On the frontend, keep using the generic data APIs in `app-rn/src/request/api.ts`. Pages are responsible for passing the right family/user scope in `search_condition`, `args`, and inserted rows. Use `useFamilyStore((s) => s.getFamilyId())` for the current family id and `useUserStore((s) => s.getUserId())` for the current user id. ### 5. Family Feed Recording Certain actions must call `recordFeed()` from `@/helpers/feed`. Use `{nickname}` placeholder in content — do NOT hardcode nicknames. The `loadFeeds()` helper replaces it dynamically. Scenarios: creating/joining family (`join`), renaming (`rename`), recording bills (`bill`), favoriting apps (`fav`). ### 6. Frontend: Use Existing Libraries Only Before writing any page/component, check `app-rn/package.json` for available dependencies. Use HeroUI Native for UI, `@legendapp/list` for long lists, `lucide-react-native` for icons, `sonner-native` for toasts. Use NativeWind `className` for styling, not inline `style={}`. ### 7. Loading State: Always Use useLoading + LoadingOverlay Any async save/update/delete operation must use the `useLoading` hook + `` pattern — never roll per-field loading booleans. The hook provides `startLoading(message)` (shows toast.loading + page mask), `stopLoading(options?)` (dismisses toast, optionally shows success/error), and `isLoading` (boolean for disabling buttons). Place `` near the bottom of the page JSX (before closing ``). Reference: `src/hooks/useLoading.ts`, `src/components/LoadingOverlay.tsx`. ### 8. Finance Convention `type = 0` means expense, `type = 1` means income. Category icon names match iconfont names (e.g., `finance_jiaotong`, `finance_canyin`). ## Key Files | File | Role | |---|---| | `PROJECT_CONTEXT.md` | Full project documentation, risks, feature status | | `app-go/internal/bootstrap/app.go` | Backend initialization flow | | `app-go/internal/router/router.go` | Route definitions | | `app-go/pkg/db/meta_field.go` | Audit field auto-injection | | `app-rn/src/app/index.tsx` | Startup/auth routing logic | | `app-rn/src/request/index.ts` | Alova instance, auth interceptor | | `app-rn/src/request/api.ts` | API wrapper functions | | `app-rn/src/store/` | Zustand stores (user, family, finance, app, favorite) | | `app-rn/src/layouts/` | Shell layouts (AppLayout, ModuleLayout, PageLayout) | | `app-rn/src/configs/pages.ts` | Sub-app definitions | | `app-rn/src/hooks/useLoading.ts` | Loading state hook (toast + overlay) | | `app-rn/src/components/LoadingOverlay.tsx` | Full-page loading mask component | | `app-rn/src/helpers/feed.ts` | Family feed recording | ## Known Risks - Generic data APIs accept table names and raw SQL from the frontend — needs server-side whitelisting before production. - `db.local.yml` contains database credentials and should never be committed. - Finance category queries currently don't filter by `family_id`. - Main tab `/space` route is missing its page file. - `GetUniqueIdReq.Count` uses `binding` tag instead of `validate` tag — min/max rules may not run.