An open API service indexing awesome lists of open source software.

https://github.com/wardvisual/im-business-suite


https://github.com/wardvisual/im-business-suite

Last synced: 14 days ago
JSON representation

Awesome Lists containing this project

README

          

# Go Business Suite — Comprehensive Boilerplate

> A production-minded starter codebase for a multi-tenant B2B "suite" web app written in **Go**. This repo is designed as a single codebase that starts small ("Suite Lite") and scales to a full platform: auth, RBAC, multi-tenancy, modules, API versioning, background jobs, CI/CD, Docker, monitoring hooks, and billing hooks.

---

## Goals

* Provide a clean, modular structure for adding modules (CRM, Invoicing, Projects, Analytics).
* Include baked-in multi-tenant patterns and role-based access control.
* Include practical infrastructure config: Docker, docker-compose, CI workflow, and production hints.
* Keep `README` friendly so GitHub Copilot / automation can scaffold the rest.

---

## Folder layout (recommended)

```
/go-business-suite
├── cmd/
│ └── api/ # main program entry for API
│ └── main.go
├── internal/
│ ├── config/ # configuration loader (env, file)
│ ├── server/ # HTTP server & router setup
│ ├── db/ # DB connection, migrations wrapper
│ ├── auth/ # authentication, JWT, password hashing
│ ├── middleware/ # request middleware (tenant, rbac, logging)
│ ├── modules/
│ │ ├── crm/ # sample module (contacts, leads)
│ │ └── invoicing/ # sample invoice module skeleton
│ ├── jobs/ # background jobs queue workers
│ └── telemetry/ # metrics, tracing, logging setup
├── migrations/ # SQL migration files (golang-migrate)
├── scripts/ # helper scripts (generate, lint, db seed)
├── deployments/ # k8s / helm / svc manifests (optional)
├── .github/workflows/ # CI pipeline
├── docker-compose.yml
├── Dockerfile
├── Makefile
├── go.mod
├── go.sum
├── .env.example
└── README.md
```

---

## Key design decisions (technical summary)

1. **HTTP framework**: `gin-gonic/gin` for minimal ceremony + performance.
2. **DB**: `PostgreSQL` as primary persistent store.
3. **ORM / Query**: `GORM` initially for rapid development. Move to `sqlc` if you want stronger typed SQL later.
4. **Migrations**: `golang-migrate/migrate`.
5. **Auth**: JWT access tokens with refresh tokens persisted in DB, password hashing with `bcrypt`.
6. **Multi-tenancy**: Row-level tenancy using `organization_id` on business tables + middleware that sets `current_tenant` from auth token or subdomain.
7. **RBAC**: Role-based access control (Owner, Admin, Member, Guest) implemented as middleware with permission checks on handlers.
8. **Background jobs**: Redis-based queue (e.g., `asynq`) for email, invoices, exports.
9. **Storage**: S3-compatible (MinIO/AWS S3) for file uploads; UploadCare or direct S3.
10. **Payments**: Stripe webhooks handler skeleton in `modules/billing`.
11. **Observability**: Sentry for errors + Prometheus metrics endpoint.
12. **CI/CD**: Build/test on push via GitHub Actions, publish Docker image to registry.

---

## .env.example

```env
# App
APP_ENV=development
APP_PORT=8080
APP_HOST=0.0.0.0
APP_BASE_URL=http://localhost:8080
JWT_SECRET=replace_with_secure_random
JWT_EXPIRATION_MINUTES=60
REFRESH_TOKEN_EXPIRY_DAYS=14

# Database (Postgres)
DATABASE_URL=postgres://postgres:postgres@db:5432/business_suite?sslmode=disable

# Redis (jobs & cache)
REDIS_URL=redis://redis:6379/0

# S3 / Object storage
S3_ENDPOINT=http://minio:9000
S3_REGION=us-east-1
S3_BUCKET=business-suite
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin

# Stripe
STRIPE_API_KEY=
STRIPE_WEBHOOK_SECRET=

# Sentry (optional)
SENTRY_DSN=

# Telemetry
PROMETHEUS_METRICS_PORT=2112

```

---

## Makefile (selected targets)

```makefile
.PHONY: build run lint fmt migrate up down test docker-build docker-up

build:
go build -o bin/api ./cmd/api

run: build
./bin/api

fmt:
gofmt -s -w .

lint:
golangci-lint run

migrate-up:
migrate -path=./migrations -database "${DATABASE_URL}" up

migrate-down:
migrate -path=./migrations -database "${DATABASE_URL}" down

docker-build:
docker build -t business-suite:local .

docker-up:
docker-compose up --build

test:
go test ./... -v
```

---

## docker-compose.yml (dev ready)

```yaml
version: '3.8'
services:
db:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=business_suite
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data

redis:
image: redis:7
ports:
- "6379:6379"

minio:
image: minio/minio
command: server /data
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
ports:
- "9000:9000"
volumes:
- minio-data:/data

api:
build: .
env_file: .env.example
depends_on:
- db
- redis
- minio
ports:
- "8080:8080"
command: ["/bin/api"]

volumes:
db-data:
minio-data:
```

---

## Dockerfile

```dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN apk add --no-cache git
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/api ./cmd/api

FROM alpine:latest
RUN apk add --no-cache ca-certificates
COPY --from=builder /bin/api /bin/api
EXPOSE 8080
CMD ["/bin/api"]
```

---

## go.mod (example)

```
module github.com/wardvisual/im-business-suite

go 1.21

require (
github.com/gin-gonic/gin v1.10.0
gorm.io/gorm v1.27.0
gorm.io/driver/postgres v1.6.1
github.com/golang-migrate/migrate/v4 v4.15.2
github.com/golang-jwt/jwt/v5 v5.0.0
golang.org/x/crypto v0.12.0
github.com/hibiken/asynq v0.26.0
github.com/getsentry/sentry-go v0.17.0
github.com/sirupsen/logrus v1.10.0
github.com/prometheus/client_golang v1.14.0
)
```

---

## Key code files

Below are minimal but complete examples for core files. Place them in the structure above.

### `cmd/api/main.go`

```go
package main

import (
"log"
"net/http"
"os"

"github.com/wardvisual/im-business-suite/internal/config"
"github.com/wardvisual/im-business-suite/internal/server"
)

func main() {
cfg, err := config.Load()
if err != nil {
log.Fatalf("failed loading config: %v", err)
}

r := server.New(cfg)
addr := cfg.AppHost + ":" + cfg.AppPort
log.Printf("listening on %s", addr)
if err := http.ListenAndServe(addr, r); err != nil {
log.Fatalf("server failed: %v", err)
}
}
```

---

### `internal/config/config.go`

```go
package config

import (
"os"
)

type Config struct {
AppEnv string
AppPort string
AppHost string
JWTSecret string
DatabaseURL string
RedisURL string
}

func Load() (*Config, error) {
c := &Config{
AppEnv: os.Getenv("APP_ENV"),
AppPort: os.Getenv("APP_PORT"),
AppHost: os.Getenv("APP_HOST"),
JWTSecret: os.Getenv("JWT_SECRET"),
DatabaseURL: os.Getenv("DATABASE_URL"),
RedisURL: os.Getenv("REDIS_URL"),
}
// TODO: validate required fields and return error if missing
return c, nil
}
```

---

### `internal/server/server.go`

```go
package server

import (
"github.com/gin-gonic/gin"
"github.com/wardvisual/im-business-suite/internal/config"
"github.com/wardvisual/im-business-suite/internal/middleware"
"github.com/wardvisual/im-business-suite/internal/modules/crm"
)

func New(cfg *config.Config) *gin.Engine {
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.Logger())

// global middlewares
r.Use(middleware.CORSMiddleware())
r.Use(middleware.TenantMiddleware()) // sets tenant context

api := r.Group("/api")
v1 := api.Group("/v1")

// auth routes
// TODO: mount auth handlers

// sample CRM module
crm.RegisterRoutes(v1)

return r
}
```

---

### `internal/middleware/tenant.go`

```go
package middleware

import (
"net/http"

"github.com/gin-gonic/gin"
)

// TenantMiddleware extracts tenant info from JWT or header and stores it in context.
func TenantMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Example: prefer header X-Tenant-ID otherwise from JWT claim
tenant := c.GetHeader("X-Tenant-ID")
if tenant == "" {
// TODO: parse JWT and extract organization_id claim
}
if tenant == "" {
// no tenant found — reject or set to public tenant depending on your policy
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "tenant required"})
return
}
c.Set("tenant_id", tenant)
c.Next()
}
}
```

---

### `internal/modules/crm/handler.go` (sample)

```go
package crm

import (
"net/http"

"github.com/gin-gonic/gin"
)

func RegisterRoutes(rg *gin.RouterGroup) {
r := rg.Group("/crm")
r.GET("/contacts", listContacts)
r.POST("/contacts", createContact)
}

func listContacts(c *gin.Context) {
tenant, _ := c.Get("tenant_id")
// fetch contacts filtered by tenant
c.JSON(http.StatusOK, gin.H{"tenant": tenant, "contacts": []string{}})
}

func createContact(c *gin.Context) {
// parse, validate, save with tenant id
c.JSON(http.StatusCreated, gin.H{"status": "ok"})
}
```

---

### `internal/auth/jwt.go` (simple helpers)

```go
package auth

import (
"time"

"github.com/golang-jwt/jwt/v5"
)

func CreateAccessToken(secret string, subject string, tenantID string, minutes int) (string, error) {
claims := jwt.MapClaims{}
claims["sub"] = subject
claims["org"] = tenantID
claims["exp"] = time.Now().Add(time.Minute * time.Duration(minutes)).Unix()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}

func ParseToken(secret string, tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
}
```

---

## Database migration example (migrations/0001_init.up.sql)

```sql
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

CREATE TABLE user_organizations (
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
org_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
role TEXT NOT NULL,
PRIMARY KEY(user_id, org_id)
);

CREATE TABLE contacts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
org_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
email TEXT,
phone TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
```

And the corresponding `down.sql` should drop these tables in reverse order.

---

## CI: .github/workflows/ci.yml (basic)

```yaml
name: CI
on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install deps
run: go mod download
- name: Run linters
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run
- name: Run tests
run: go test ./... -v
- name: Build
run: go build -o bin/api ./cmd/api
```

---

## Observability quick hooks

* Expose `/metrics` for Prometheus using `promhttp.Handler()` in telemetry.
* Capture panics and forward to Sentry with request context.
* Log structured logs via `logrus` (or zap) — keep logs JSON for production.

---

## Notes on multi-tenancy & data isolation strategies

* **Row-level tenancy** (recommended for small-medium scale): add `org_id` column to business tables and enforce filter in queries and DB indices. Simpler to start.
* **Schema-per-tenant** (higher isolation): create a separate schema per tenant (Postgres) — more complex for migrations.
* **DB-per-tenant** (highest isolation): separate DB instance per tenant — heavy management.

Start with row-level tenancy; audit access at middleware to ensure no cross-tenant leakage.

---

## Security checklist for launch (minimum)

* HTTPS only (TLS certs in production)
* JWT secret from secure store (Vault or env var in CI secret)
* Passwords hashed with bcrypt and strong pepper/salt policy
* Input validation and prepared statements / ORM usage to avoid SQL injection
* Rate limiting (IP-based and user-based)
* File upload virus scanning or type checks (avoid storing executable content)
* Audit logs for admin actions
* Backups & automated backup tests

---

## Next steps & expansion ideas

1. Add `modules/invoicing` with Stripe integration and webhook handling. Save invoices with `org_id`.
2. Add `modules/analytics` that ingests events using `asynq` workers and stores time-series metrics.
3. Add an internal API gateway or GraphQL layer for aggregated data across modules.
4. Add white-labeling: per-organization branding config, custom domain routing (CNAME + tenant detection by hostname).

---

## README.md (for your repo root)

Below is a friendly README you can paste into `README.md` in the GitHub repo. It includes steps so Copilot can read and act on it.

---

# Business Suite — Boilerplate (README)

**Quick start**

1. Copy repository to your GitHub (or `git clone` this starter).
2. Create `.env` from `.env.example` and set secrets (e.g., JWT_SECRET, DATABASE_URL).
3. `make docker-up` to bring up Postgres, Redis, MinIO and the API (dev).
4. Run DB migrations: `make migrate-up` (ensure `DATABASE_URL` in `.env` points to your dev DB).
5. Start local server for development: `make run` or `go run ./cmd/api`.

**Development notes**

* Use `POST /api/v1/auth/register` to create first user (seed function can create first org & owner).
* Use header `X-Tenant-ID` for the tenant in development if your token doesn't carry org claim.
* Use `curl` or Postman for quick API exploration.

**Testing**

* Unit tests: `go test ./... -v`
* Integration tests: add docker-compose override and use TestMain to run migrations and seed test data.

**PR / Branching**

* `main` for production-ready code.
* `develop` for active dev.
* Feature branches: `feature/`.

**Production checklist (before launch)**

* Move secrets to Vault or cloud secret manager.
* Set up TLS (Let’s Encrypt / cloud load balancer).
* Backups & restore drills for Postgres & object store.
* Configure autoscaling and health checks (k8s HPA / cloud autoscale).
* Legal/compliance: data residency, privacy policy, and TOS.

**Roadmap**

* MVP: Auth + CRM Lite (Contacts & Leads) + Invoicing basic.
* v1: Billing + Dashboard + Team & Permissions.
* v2: App marketplace (webhooks + OAuth for third-party integration).

**Contact & Contributing**

* Add issues, feature requests, or PRs.
* Use GitHub Actions to run linters and tests on PRs.

---

## Final notes

This boilerplate is intentionally opinionated to get you shipping quickly while leaving room for production concerns. Copy the files above into your repository structure and iterate — start with `auth` + `tenant` middleware + the `crm` module to validate the concept with 1–2 customers.

If you want, I can now:

* Generate a full ZIP of all these files (one-shot skeleton) with full code for each file.
* Or produce full, fleshed-out implementations for specific packages (auth, db, middleware) one at a time.

Tell me which one you want next: `zip skeleton`, `full auth implementation`, or `db + migrations scripts`.