feat(go-crm): full auth, routing, middleware, and supporting infra
- Add auth handlers (signup, login, logout, account management) with bcrypt - Add client, customer, service, scheduling, payment, question, answer handlers - Add dashboard, monthly report, and lead pipeline pages - Add UTF-8 middleware to force charset on HTML responses - Add config package with env-based overrides for DB path, secrets, endpoints - Add parser package for WhatsApp message ingestion - Add clean-arch layers: pkg/domain, pkg/repo, pkg/usecase for leads - Add cmd/migrate utility for DB migrations - Add Makefile, README, run-tests.sh, and dev scripts - Update docker-compose.yml with memory limits - Update .air.toml to exclude DB files and stop on errors - Update whatsapp-sync dependencies and add src/index.js entrypoint - Add whatsme standalone WhatsApp reader app (source only) - Untrack .opencode-sandbox/data/go-crm.db from git history - Expand root .gitignore: ngrok, tmp dirs, sandbox DBs, compiled binaries
This commit is contained in:
125
apps/go-crm/README.md
Normal file
125
apps/go-crm/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# go-crm (lightweight local dev)
|
||||
|
||||
This service is intentionally small so junior Go developers can understand it end-to-end without Docker, hot reloads, or other heavy tooling. The goal: `go run main.go` with a single SQLite file in `.dev-data/`.
|
||||
|
||||
## Local development (junior-friendly)
|
||||
|
||||
1. Ensure Go 1.25+ is installed and `go` is in your `PATH`.
|
||||
2. Run the helper script (no Docker):
|
||||
```bash
|
||||
./scripts/dev.sh
|
||||
```
|
||||
3. The server boots on [http://localhost:8080](http://localhost:8080).
|
||||
4. The database and WhatsApp store live under `.dev-data/` (auto-created).
|
||||
|
||||
There are no file watchers or `air` hot reloads—just the plain Go toolchain so newcomers can focus on code, not tooling.
|
||||
|
||||
## Project layout (modern, clean layers)
|
||||
|
||||
```
|
||||
go-crm/
|
||||
├── cmd/ # optional commands (e.g. migrations)
|
||||
│ └── migrate/ # lightweight migration CLI (future)
|
||||
├── config/ # templated configs (kept simple)
|
||||
├── data/ # production data path (gitignored)
|
||||
├── internal/ # HTTP handlers (usecases wired to HTTP)
|
||||
│ ├── handlers/
|
||||
│ ├── middleware/
|
||||
│ └── templates/
|
||||
├── pkg/ # reusable application packages
|
||||
│ ├── domain/ # entities + repository interfaces
|
||||
│ ├── usecase/ # application services / business logic
|
||||
│ └── repo/ # persistence adapters (SQLite, etc.)
|
||||
├── scripts/ # helper scripts (dev runner, migrations)
|
||||
├── main.go # application entrypoint (wire handlers → usecases)
|
||||
├── go.mod
|
||||
└── go.sum
|
||||
```
|
||||
|
||||
## Recommended architecture / interfaces
|
||||
|
||||
### `pkg/domain/lead.go`
|
||||
```go
|
||||
package domain
|
||||
|
||||
// Lead is the core business entity.
|
||||
type Lead struct {
|
||||
LeadID int64
|
||||
ClientID int64
|
||||
Name string
|
||||
PhoneRaw string
|
||||
PhoneNormalized string
|
||||
ServiceInterest string
|
||||
Status string
|
||||
PaymentStatus string
|
||||
}
|
||||
|
||||
// LeadRepository abstracts persistence.
|
||||
type LeadRepository interface {
|
||||
FindAll(ctx context.Context, clientID int64) ([]Lead, error)
|
||||
FindByID(ctx context.Context, clientID, leadID int64) (*Lead, error)
|
||||
Update(ctx context.Context, lead Lead) error
|
||||
}
|
||||
```
|
||||
|
||||
### `pkg/usecase/lead_service.go`
|
||||
```go
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go-crm/pkg/domain"
|
||||
)
|
||||
|
||||
// LeadService orchestrates business logic.
|
||||
type LeadService struct {
|
||||
Repo domain.LeadRepository
|
||||
}
|
||||
|
||||
func (s *LeadService) ListAll(ctx context.Context, clientID int64) ([]domain.Lead, error) {
|
||||
return s.Repo.FindAll(ctx, clientID)
|
||||
}
|
||||
```
|
||||
|
||||
### `pkg/repo/sqlite_lead_repo.go`
|
||||
```go
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"go-crm/pkg/domain"
|
||||
)
|
||||
|
||||
// SQLiteLeadRepository implements LeadRepository using sqlite.
|
||||
type SQLiteLeadRepository struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func (r *SQLiteLeadRepository) FindAll(ctx context.Context, clientID int64) ([]domain.Lead, error) {
|
||||
// query leads table, scan rows, return []domain.Lead
|
||||
}
|
||||
```
|
||||
|
||||
Handlers in `internal/handlers` should accept a clear service layer (`usecase.LeadService`), not raw DB logic. Keep controllers thin: parse request, call service, write HTML/JSON.
|
||||
|
||||
## Migration strategy
|
||||
|
||||
- Do not scan/clean the entire `leads` table on every startup (that was causing RAM blowups).
|
||||
- Run the lightweight CLI with `make migrate` (which builds and runs the migration binary) whenever you need to refresh the schema or re-decode legacy Latin-1 data. To target non-default files, run the migration binary directly:
|
||||
```bash
|
||||
go build -o migrate cmd/migrate/main.go && ./migrate --db /path/to/go-crm.db
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
- No Docker or `air` watchers—just `go run main.go`.
|
||||
- Clean package boundaries (domain/usecase/repo) improve readability and testability.
|
||||
- Scripts under `scripts/` orchestrate database setup or helper tasks.
|
||||
- Use the architecture sketch above to guide future refactors.
|
||||
Reference in New Issue
Block a user