Parcel App is a multi-surface platform for product discovery, checkout, parcel dispatch, courier workflows, vendor operations, complaints handling, and administrative control. This repository contains the backend services and client applications that together support customer shopping, vendor fulfillment, courier dispatch, payment processing, and internal operations.
The repository is organized as a practical product suite rather than a single compiled workspace. Each folder is an independently runnable application or service, but they are designed to work together around a shared business domain and a shared authentication model.
- What This Repository Contains
- Platform Architecture
- Repository Map
- How The System Works
- Authentication and Session Model
- Recommended Local Development Order
- Environment Configuration
- Run Commands By Project
- Quality Checks and Validation
- Operational Notes and Current State
- Cloud Infrastructure
- Security Model
- Troubleshooting Guide
- Suggested Contributor Workflow
This repository currently holds six major parts:
parcel-app-service: the main Django backend and API platformparcel-app-web: the primary Next.js web client using cookie-session authparcel-app-admin: the admin and operations console for internal workflowsparcel-app-mobile: the Expo/React Native mobile client for customer, vendor, and courier flowsparcel-app-payment-service: the Spring Boot payment and order-processing serviceparcel-app-react: an older Vite-based storefront still present in the repository
If you are new to the codebase, the fastest way to understand the current product direction is:
- Start with
parcel-app-service - Then read
parcel-app-web - Then review
parcel-app-admin - Use
parcel-app-payment-servicefor payment-specific work - Treat
parcel-app-reactas legacy unless your task explicitly targets it
+----------------------+
| parcel-app-admin |
| React + Vite |
| internal operations |
+----------+-----------+
|
|
+----------------------+ +-----------v-----------+ +----------------------+
| parcel-app-mobile | | parcel-app-service | | parcel-app-payment- |
| Expo + React Native +--------->+ Django + DRF +--------->+ service |
| customer/vendor/ | REST | auth, users, orders, | payment | Spring Boot + |
| courier interfaces | | dispatch, products | and | PostgreSQL |
+----------------------+ +-----------+-----------+ orders +----------------------+
^
|
|
+----------+-----------+
| parcel-app-web |
| Next.js + Zustand |
| primary web client |
+----------------------+
+----------------------+
| parcel-app-react |
| Vite storefront |
| legacy/alternate UI |
+----------------------+
parcel-app-serviceis the operational center of the platform.parcel-app-webis the most up-to-date browser client for authenticated customer, vendor, and courier experiences.parcel-app-adminis for internal staff and operational control.parcel-app-payment-servicehandles payment-oriented workflows in a separate Java service.parcel-app-mobilemirrors core user journeys on native devices.parcel-app-reactremains in the repository as an older frontend surface and should be approached carefully before adding new feature work.
parcel-app/
├── parcel-app-admin/ # Internal admin console for moderation and ops
├── parcel-app-mobile/ # Expo mobile client for customer/vendor/courier journeys
├── parcel-app-payment-service/ # Spring Boot payment service with PostgreSQL
├── parcel-app-react/ # Older Vite-based storefront and payment UI
├── parcel-app-service/ # Main Django backend and domain APIs
├── parcel-app-web/ # Current Next.js web application
└── terraform/ # AWS infrastructure as code (Terraform)
├── envs/dev/ # Development environment stack
├── envs/staging/ # Staging environment stack
├── envs/prod/ # Production environment stack
└── modules/ # Reusable modules (networking, ECS, Aurora, …)
| Project | Role | Main Stack | Status |
|---|---|---|---|
parcel-app-service |
Core business API | Django, DRF, Celery, PostgreSQL | Active |
parcel-app-web |
Primary web product | Next.js 16, React 19, Zustand | Active |
parcel-app-admin |
Operations console | React 19, Vite, Vitest | Active |
parcel-app-mobile |
Mobile product surfaces | Expo 54, React Native 0.81, Zustand | Active, but config centralization is still needed |
parcel-app-payment-service |
Payment/order service | Spring Boot 3.4, Java 17, PostgreSQL | Active |
parcel-app-react |
Older storefront | Vite, React 18, Zustand | Legacy/transitionary |
At a high level, the platform supports these business flows:
- A customer browses products on web or mobile.
- Cart and checkout requests are coordinated through the platform APIs.
- Payment workflows may involve the payment service.
- Orders move into fulfillment and dispatch.
- Delivery and resolution status are surfaced back to the user.
- A vendor manages products, stock, and deals.
- Vendor-side order and notification views support fulfillment.
- Vendor transaction and resolution screens track commercial outcomes.
- Couriers authenticate into dedicated dashboards.
- Dispatch and deal assignment are surfaced to the courier channel.
- Delivery execution and transaction views track active work.
- Internal staff monitor moderation, dispatch, complaints, banking, and orders.
- The admin app acts as an operational console rather than a public product.
- Quality gates are already in place for linting, regression tests, and release builds.
The current source of truth for web authentication is the Django backend in parcel-app-service.
- The backend issues an
auth_sessioncookie after successful login. - The backend exposes
GET /auth/me/as the trusted identity bootstrap endpoint. - The frontend stores only minimal in-memory auth state.
- Sensitive auth state is not persisted in localStorage.
- CSRF is required for unsafe cookie-authenticated requests.
- Active role switching is supported for users with multiple approved accounts under the same email.
| Endpoint | Purpose |
|---|---|
GET /auth/me/ |
Bootstrap current authenticated identity |
GET /auth/csrf/ |
Issue or refresh CSRF token for cookie-authenticated mutations |
POST /auth/api/logout/ |
Invalidate active session and clear cookies |
POST /auth/switch-role/ |
Change active role for multi-role users |
POST /auth/customer/login/ |
Customer login |
POST /vendors/login/ |
Vendor login |
POST /couriers/login/ |
Courier login |
For parcel-app-web, route protection now depends on backend-trusted session resolution rather than frontend markers alone. If client state and backend state disagree, the backend wins.
If you are changing login, logout, dashboards, middleware, or role switching, you should reason from parcel-app-service and /auth/me/ first, then verify how the frontend hydrates and routes.
This repository is easiest to run when you start services in dependency order.
Use this when you are working on the main product path.
- Start
parcel-app-service - Start
parcel-app-payment-serviceif your flow touches checkout or payment verification - Start
parcel-app-web - Start
parcel-app-adminif you need internal operations workflows - Start
parcel-app-mobileonly if you are testing native flows
Use this only if your task explicitly targets the older Vite storefront.
- Start
parcel-app-service - Start
parcel-app-payment-service - Start
parcel-app-react
The repository does not use one shared root .env. Each application manages its own configuration.
Core backend variables documented in .env.example include:
DJANGO_SECRET_KEY=replace-with-your-secret-key
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_CORS_ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
DJANGO_CORS_ALLOW_CREDENTIALS=True
DJANGO_SESSION_COOKIE_SECURE=False
DJANGO_SESSION_COOKIE_SAMESITE=Lax
DJANGO_CSRF_COOKIE_SECURE=False
DJANGO_CSRF_COOKIE_SAMESITE=Lax
DJANGO_AUTH_SESSION_COOKIE_SECURE=False
DJANGO_AUTH_SESSION_COOKIE_SAMESITE=Lax
DJANGO_AUTH_MARKER_COOKIE_SECURE=False
DJANGO_AUTH_MARKER_COOKIE_SAMESITE=LaxOperational notes:
CORS_ALLOWED_ORIGINSalone is not enough for browser auth flows.CSRF_TRUSTED_ORIGINSmust also include the frontend origin.- Localhost development typically works with
Laxand insecure cookies. - Cross-site production deployments usually need
SecureplusSameSite=None.
Recommended frontend variables:
NEXT_PUBLIC_API_BASE=http://localhost:7000
NEXT_PUBLIC_PAYMENT_API_BASE=http://localhost:8080Required variable:
VITE_API_BASE_URL=http://localhost:7000This service is primarily configured through Spring property files and profile selection.
Key defaults documented in the service README:
- development profile:
application-dev.properties - production profile:
application-prod.properties - default runtime port:
8080 - PostgreSQL required
Current state:
- the mobile app is Expo-based and runnable locally
- several screens currently call
http://localhost:7000/...directly - endpoint configuration is not yet fully centralized behind one environment abstraction
That means mobile work on a physical device usually requires replacing localhost with a LAN IP or introducing a proper shared API config layer.
cd parcel-app-service
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python manage.py migrate
python manage.py runserver 7000cd parcel-app-payment-service
./gradlew clean build
./gradlew bootRun --args='--spring.profiles.active=dev'cd parcel-app-web
npm install
npm run devcd parcel-app-admin
npm install
cp .env.example .env
npm run devcd parcel-app-mobile
npm install
npm run startOther useful mobile commands:
npm run android
npm run ios
npm run webcd parcel-app-react
npm install
npm run devUse project-local checks rather than assuming one root command exists.
| Project | Useful Commands |
|---|---|
parcel-app-admin |
npm run lint, npm run test, npm run build, npm run check |
parcel-app-web |
npm run test, npm run build |
parcel-app-react |
npm run test, npm run build |
parcel-app-service |
python manage.py check, python manage.py test |
parcel-app-payment-service |
./gradlew test, ./gradlew clean build |
parcel-app-mobile |
manual app verification via Expo flows |
- Run Django checks and relevant Django tests.
- Run
parcel-app-webtests and build. - If internal workflows changed, run
parcel-app-adminchecks. - If checkout changed, run payment-service build and endpoint validation.
- If native flows changed, verify at least one customer and one role-specific mobile path.
parcel-app-serviceis the core backend source of truth.parcel-app-webis the primary browser client and has the latest auth architecture work.parcel-app-adminhas release-quality checks and regression coverage in place.
parcel-app-reactis still present but represents an older frontend direction.parcel-app-mobilecurrently contains hardcoded localhost API references in multiple screens, which makes device testing more fragile than browser-based local development.parcel-app-payment-serviceruns as a separate Java service and requires its own PostgreSQL setup and profile management.
This is a product suite with multiple clients around one business domain, not a single framework app split into packages.
The terraform/ directory contains a production-ready, modular Terraform layout that provisions the full Parcel App stack on AWS. Every service is independently toggleable, so you can bring up a partial stack (frontend only, backend only, data tier only) or the full platform in one pass.
┌──────── Internet ─────────────────────────────────────────┐
│ │
Browsers/Apps │ Mobile (Expo) │
│ │
┌───────────▼──────────┐ ┌──────────────────────────────┐ │
│ CloudFront │ │ Route 53 │ │
│ CDN + WAF edge │ │ (optional custom domains) │ │
│ web / admin assets │ └──────────────┬───────────────┘ │
└───────────┬──────────┘ │ │
│ /api/* /auth/* │ HTTPS │
│ /admin/* forwarded to ALB │ │
┌───────────▼──────────────────────────────────────▼──────────────┐ │
│ Application Load Balancer (ALB) │ │
│ HTTPS :443 ─ path-based routing ─ target groups │ │
└────────┬────────────────────┬───────────────────┬────────────────┘ │
│ /api/* /auth/* │ /payments/* │ /paystack/* │
┌────────▼────────┐ ┌────────▼──────────┐ ┌──────▼──────────────────┐│
│ ECS Fargate │ │ ECS Fargate │ │ ECS Fargate ││
│ django-api │ │ payment-service │ │ celery-worker ││
│ (Django DRF) │ │ (Spring Boot) │ │ celery-beat ││
└────────┬────────┘ └────────┬──────────┘ └─────────────────────────┘│
│ │ │
┌────────────┼────────────────────┼──────────────────────┐ │
│ │ Data Tier │ │ │
│ ┌────────▼────────┐ ┌────────▼──────────┐ ┌────────▼──────────────┐ │
│ │ Aurora Postgres │ │ ElastiCache Redis │ │ SQS + Dead-letter │ │
│ │ Serverless v2 │ │ (session / cache) │ │ queue │ │
│ └─────────────────┘ └───────────────────┘ └───────────────────────┘ │
│ │
│ Supporting Services │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ECR repos · Secrets Manager · CloudWatch · X-Ray · SNS alerts │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘
Static hosting:
┌───────────────────────────────────────────────────────────────────────────┐
│ S3 (parcel-app-web) ──► CloudFront distribution ──► browsers │
│ S3 (parcel-app-admin) ──► CloudFront distribution ──► internal staff │
└───────────────────────────────────────────────────────────────────────────┘
Networking:
┌─────────────────────────────────────────────────────────────────────┐
│ VPC (10.0.0.0/16) │
│ Public subnets → ALB, NAT Gateways │
│ Private subnets → ECS tasks, Aurora, ElastiCache │
└─────────────────────────────────────────────────────────────────────┘
| Module | What it provisions |
|---|---|
networking |
VPC, public/private subnets across AZs, IGW, NAT gateways, route tables, VPC flow logs |
security_groups |
Least-privilege SGs for ALB, ECS tasks, Aurora cluster, Redis, and inter-service rules |
alb |
Application Load Balancer, HTTPS/HTTP listeners, path-based routing, access logs |
ecs_cluster |
ECS Fargate cluster with Container Insights enabled |
ecs_service |
Reusable template for any Fargate service — task definition, service, target group, listener rule, autoscaling |
ecr |
Container registries for Django (API + workers) and the payment service; lifecycle policies |
aurora |
Aurora PostgreSQL Serverless v2 cluster, subnet group, parameter group, automated backups |
elasticache |
Redis replication group with TLS in-transit auth token, subnet group |
sqs |
Standard work queue + dead-letter queue, queue policies scoped to app task roles |
secrets |
Secrets Manager entries for DB passwords, Django secret key, Paystack key, SMTP password, Redis auth token |
s3_frontend |
S3 buckets for Next.js and admin static builds; versioning and public-access blocks |
cloudfront |
CloudFront distributions for web and admin buckets with origin access control; API passthrough to ALB |
monitoring |
CloudWatch alarms (ECS CPU/memory, ALB 5xx, Aurora CPU), SNS alert topic, CloudWatch dashboard, X-Ray group |
terraform/
├── envs/
│ ├── dev/ ← development — single NAT, spot opt-in, low RCU/WCU
│ ├── staging/ ← pre-production — mirrors prod topology at reduced scale
│ └── prod/ ← production — multi-AZ NAT, deletion protection, full HA
└── modules/ ← shared modules consumed by all three envs
Each environment has its own:
backend.tf— remote state in a dedicated S3 prefix with DynamoDB lockingterraform.tfvars.example— copy and fill to match your target account/region- independently tunable resource sizing (CPU, memory, min/max autoscaling capacity)
Every major component is guarded by a boolean flag in terraform.tfvars. Set false to exclude it from the plan and apply entirely.
# terraform/envs/dev/terraform.tfvars (example)
enable_frontend_web = true # Next.js S3 + CloudFront
enable_frontend_admin = true # Admin React app S3 + CloudFront
enable_backend_api = true # Django API on ECS Fargate
enable_payment_service = true # Spring Boot payment service on ECS Fargate
enable_celery_worker = true # Async task worker
enable_celery_beat = false # Scheduled task runner (off in dev)
enable_aurora = true # Aurora PostgreSQL Serverless v2
enable_redis = true # ElastiCache Redis
enable_sqs = true # SQS work queue + DLQ
enable_monitoring = true # CloudWatch alarms + dashboardThis means you can budget-control a portfolio demo by deploying only the web frontend and backend API, skipping the data tier or monitoring until needed.
- Terraform >= 1.5.0 installed
- AWS CLI configured (
aws configure) with an IAM user or role that has sufficient permissions - Verify your identity:
aws sts get-caller-identity
Before running terraform init for the first time, create the remote state resources:
# Create the S3 state bucket (adjust name to match terraform/envs/<env>/backend.tf)
aws s3api create-bucket \
--bucket parcel-terraform-state \
--region us-east-1
aws s3api put-bucket-versioning \
--bucket parcel-terraform-state \
--versioning-configuration Status=Enabled
# Create the DynamoDB lock table
aws dynamodb create-table \
--table-name parcel-terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1cd terraform/envs/dev
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars: fill in ACM cert ARNs, domain aliases, alarm email, etc.
terraform init
terraform plan
terraform applycd terraform
# Format check
./tf-all.sh fmt
# Validate all three environments (no credentials required)
./tf-all.sh validate
# Plan all three environments in a temporary backend-free workspace
./tf-all.sh planOr use the Makefile equivalents:
make fmt-all
make validate-all
make plan-all| Application | Infrastructure it uses |
|---|---|
parcel-app-web |
S3 bucket → CloudFront distribution + ALB passthrough for API calls |
parcel-app-admin |
Separate S3 bucket → CloudFront distribution |
parcel-app-service |
ECS Fargate (django-api) + Aurora + Redis + SQS + ECR |
parcel-app-payment-service |
ECS Fargate (payment-service) + Aurora + ECR |
parcel-app-mobile |
Uses the same ALB/API endpoint, no separate infra provisioned |
After a successful terraform apply:
- Push container images to the ECR repositories created by Terraform. Image URIs are in
terraform output. - Replace placeholder secrets in AWS Secrets Manager — Paystack key and SMTP password are seeded with literal placeholders.
- Force a new ECS deployment so services pick up the real container images.
- Point DNS — if using custom domains, create ALIAS/CNAME records targeting the CloudFront and ALB hostnames from
terraform output.
Full operator notes, required IAM permissions, and least-privilege setup options are documented in terraform/README.md.
The platform already contains several important security controls. Contributors should preserve them.
- cookie-authenticated session resolution on the backend
- CSRF enforcement for unsafe requests authenticated by cookie
- environment-driven cookie flags for
Secure,SameSite, and optional domains - trusted-origin configuration for browser-based requests
parcel-app-webdoes not treat localStorage as auth truth- route protection is backend-trusted through
/auth/me/ - auth teardown is backend-driven on logout
- unsafe requests fetch and attach CSRF tokens
- avoid storing session tokens in frontend storage
- do not bypass trusted-origin configuration in development by disabling CSRF casually
- prefer environment-based cookie tuning rather than hardcoding production security settings
- treat role switching as a session mutation and rehydrate from the backend after it completes
Check these in order:
- Confirm the backend is setting
auth_session. - Confirm the frontend origin is included in
DJANGO_CORS_ALLOWED_ORIGINS. - Confirm the frontend origin is included in
DJANGO_CSRF_TRUSTED_ORIGINS. - Confirm
GET /auth/me/returns the expectedactive_role. - Confirm middleware is pointed at the correct backend base URL.
Likely causes:
- frontend did not fetch
/auth/csrf/ X-CSRFTokenwas not sent- trusted origins are incomplete
- cookie flags do not match the deployment topology
The current mobile code uses several http://localhost:7000/... endpoints directly. On a real device, localhost points to the device itself, not your computer. Use your machine's LAN IP or centralize mobile API configuration before broader device testing.
Verify:
- the client is pointed at the correct payment-service base URL
- the payment service is running with the intended Spring profile
- PostgreSQL is reachable by the payment service
- any client-specific checkout payload shape still matches backend expectations
- Start in
parcel-app-service - Validate
/auth/me/,/auth/csrf/, and login/logout behavior - Move to
parcel-app-webmiddleware and store hydration - Test role-specific dashboards and protected routes
- Review
parcel-app-payment-service - Validate PostgreSQL and Spring profile config
- Then test the calling client in
parcel-app-weborparcel-app-react
- Update
parcel-app-serviceif the API contract changes - Validate workflows in
parcel-app-admin - Run
npm run checkin the admin app before shipping
- Identify whether the screen uses hardcoded API URLs
- Replace ad hoc endpoints with shared configuration where possible
- Test on simulator first, then on a real device with network-aware base URLs
Each major app or service already has or should have its own local README for deeper details:
parcel-app-admin/README.mdparcel-app-web/README.mdparcel-app-service/README.mdparcel-app-payment-service/README.mdparcel-app-react/README.md
The root README should help you navigate the platform. The project-local READMEs should help you execute changes inside each component.