Copilot Instructions for httpSMS
httpSMS is a service that turns an Android phone into an SMS gateway via an HTTP API. This is a monorepo with three components:
- api/ — Go backend (Fiber, GORM, PostgreSQL)
- web/ — Nuxt 2 frontend (Vue 2, Vuetify 2, TypeScript)
- android/ — Native Android app (Kotlin)
Build, Test, and Lint Commands
cd api
# Development with hot-reload
air
# Build
go build -o ./tmp/main.exe .
# Run tests
go test ./...
# Run a single test
go test ./pkg/services/ -run TestMessageService
# Generate Swagger docs (required after changing API annotations)
swag init --requiredByDefault --parseDependency --parseInternal
# Pre-commit hooks run: go-fumpt, go-imports, go-lint, go-mod-tidy
cd web
# Install dependencies
pnpm install
# Development server (port 3000)
pnpm dev
# Lint (eslint + stylelint + prettier)
pnpm lint
# Auto-fix lint issues
pnpm lintfix
# Run tests (Jest)
pnpm test
# Static site generation (production build)
pnpm run generate
# Regenerate TypeScript API models from Swagger
pnpm api:models
cd android
# Build
./gradlew build
# Debug APK
./gradlew assembleDebug
# Release APK
./gradlew assembleRelease
# Start all services (PostgreSQL, Redis, API, Web)
docker compose up --build
# API at localhost:8000, Web at localhost:3000
API — Layered Architecture with Event-Driven Processing
The API uses a DI container (pkg/di/container.go) that lazily initializes all services as singletons. The layered architecture flows as:
Handlers → Services → Repositories → GORM/PostgreSQL
- Handlers (pkg/handlers/) — Fiber HTTP handlers. Each has a RegisterRoutes() method and embeds a base handler struct with standardized response methods (responseBadRequest, responseNotFound, etc.).
- Services (pkg/services/) — Business logic. Orchestrate repositories and dispatch events.
- Repositories (pkg/repositories/) — Data access via GORM. Interfaces defined alongside GORM implementations (prefixed gorm*).
- Validators (pkg/validators/) — One validator per handler, return url.Values for field errors.
- Entities (pkg/entities/) — Domain models, auto-migrated by GORM.
Event system: Uses CloudEvents spec (cloudevents/sdk-go). Events defined in pkg/events/ (31 event types). Listeners in pkg/listeners/ process events either synchronously or via Google Cloud Tasks queue (emulator mode for local dev).
Entry point: main.go loads .env in local mode, creates the DI container, and starts Fiber on APP_PORT.
- State management: Single Vuex store (store/index.ts) — actions make API calls via Axios, mutations update state, getters expose computed values.
- Components: Use vue-property-decorator class syntax with @Component, @Prop, @Watch decorators.
- API client: Axios configured in plugins/axios.ts with Firebase bearer token auth and x-api-key header support.
- API models: TypeScript types in models/ are auto-generated from the Swagger spec via swagger-typescript-api.
- Auth: Firebase Authentication (Email/Password, Google, GitHub) with auth and guest middleware for route guards.
- Real-time: Pusher.js for live message updates.
Android — Task-Oriented, Event-Driven
- No MVVM/Clean Architecture — uses a flat package structure with Activities, Services, BroadcastReceivers, and WorkManager tasks.
- FCM integration: MyFirebaseMessagingService receives push notifications → schedules SendSmsWorker via WorkManager → fetches message from API → sends SMS.
- Dual SIM support: Independent settings per SIM via Settings singleton (SharedPreferences).
- HTTP client: OkHttp with x-api-key authentication against the API.
- Encryption: AES-256/CFB with SHA-256 key derivation (Encrypter.kt).
- Error handling: Use github.com/palantir/stacktrace — wrap errors with stacktrace.Propagate(err, "context") or stacktrace.PropagateWithCode(). Never return bare errors.
- Database queries: Always use GORM query builder with context propagation (repository.db.WithContext(ctx)). No raw SQL.
- Route registration: Each handler defines RegisterRoutes() called from the DI container. Routes follow REST conventions under /v1/.
- Middleware chain: HTTP Logger → OpenTelemetry → CORS → Request Logger → Bearer Auth → API Key Auth.
- Observability: All layers are instrumented with OpenTelemetry (Fiber, GORM, Redis). Pass logger and tracer to constructors.
- Code formatting: go-fumpt (not gofmt), enforced via pre-commit hooks.
- Formatting: No semicolons, single quotes, 2-space indentation (Prettier + ESLint).
- Component style: Class-based with vue-property-decorator, not Options API (though some pages use Vue.extend()).
- Store pattern: Actions handle async API calls and commit mutations. Access store from components via this.$store.
- API calls: Use HttpSmsApiService singleton (static create() factory). OkHttp client with x-api-key header.
- Background work: Use WorkManager for tasks that must survive process death. Direct Thread { } for lightweight background ops.
- State: Settings object (SharedPreferences singleton) for all persistent state.
- Phone number formatting: Use libphonenumber for E.164 format validation.