7.8 KiB
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, schemapublic
Read PROJECT_CONTEXT.md for deeper context; this file focuses on what you need day-to-day.
Commands
Backend (app-go/)
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/)
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:
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.
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:
AppLayoutfor main shell,ModuleLayoutfor sub-app shells,PageLayoutfor 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<T> 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 + <LoadingOverlay> 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 <LoadingOverlay visible={isLoading} /> near the bottom of the page JSX (before closing </PageLayout>). 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.ymlcontains database credentials and should never be committed.- Finance category queries currently don't filter by
family_id. - Main tab
/spaceroute is missing its page file. GetUniqueIdReq.Countusesbindingtag instead ofvalidatetag — min/max rules may not run.