mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-03 20:13:00 +00:00
feat(core): consolidate auth service into backend and implement stripe billing
This commit performs a major architectural refactor by migrating the standalone `auth-service` into the main `backend` application, enabling a unified codebase and simplified deployment. It also introduces comprehensive Stripe billing support and a new administrative dashboard.
Key changes:
- **Architecture**: Deleted `apps/auth-service` and integrated its functionality (JWT, magic links, OAuth, user management) into `apps/backend`.
- **Billing**: Added Stripe integration to `backend`, supporting both monthly and yearly subscription cycles with automatic plan entitlement enforcement (e.g., location limits).
- **Admin Dashboard**: Implemented a new administrative service and API endpoints to manage tenants, users, and view platform-wide statistics.
- **Frontend**:
- Added a new pricing page with monthly/yearly toggle and comparison table.
- Integrated Stripe and Sentry for payments and error tracking.
- Improved dashboard UX/UI and added i18n support for new features.
- Enhanced the public booking flow with better validation and contact form integration.
- **Database**: Added migrations for users, magic links, password resets, OAuth states, admin audit logs, and refresh tokens.
- **DevOps**: Updated environment configurations for Railway and Vercel, and streamlined the project's `package.json` scripts.
This commit is contained in:
@@ -28,8 +28,14 @@ type Config struct {
|
||||
PaddleAPIKey string
|
||||
PaddleWebhookKey string
|
||||
PaddlePriceMatrix map[string]map[string]string
|
||||
StripeAPIKey string
|
||||
StripeWebhookKey string
|
||||
StripePriceMatrix map[string]map[string]string
|
||||
AdminEmail string
|
||||
AdminKey string
|
||||
UmamiAPIURL string
|
||||
UmamiAPIKey string
|
||||
SentryDSN string
|
||||
DemoMode bool
|
||||
}
|
||||
|
||||
@@ -53,8 +59,14 @@ func Load() (Config, error) {
|
||||
PaddleAPIKey: strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_API_KEY")),
|
||||
PaddleWebhookKey: strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_WEBHOOK_SECRET")),
|
||||
PaddlePriceMatrix: paddlePriceMatrixFromEnv(),
|
||||
StripeAPIKey: strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_API_KEY")),
|
||||
StripeWebhookKey: strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_WEBHOOK_SECRET")),
|
||||
StripePriceMatrix: stripePriceMatrixFromEnv(),
|
||||
AdminEmail: strings.TrimSpace(os.Getenv("BOOKRA_ADMIN_EMAIL")),
|
||||
AdminKey: strings.TrimSpace(os.Getenv("BOOKRA_ADMIN_KEY")),
|
||||
UmamiAPIURL: strings.TrimSpace(os.Getenv("BOOKRA_UMAMI_API_URL")),
|
||||
UmamiAPIKey: strings.TrimSpace(os.Getenv("BOOKRA_UMAMI_API_KEY")),
|
||||
SentryDSN: strings.TrimSpace(os.Getenv("BOOKRA_SENTRY_DSN")),
|
||||
DemoMode: boolFromEnv("BOOKRA_DEMO_MODE", false),
|
||||
}
|
||||
|
||||
@@ -118,6 +130,34 @@ func (cfg Config) PaddleCheckoutConfigured(planCode string) bool {
|
||||
return cfg.PaddleConfigured() && cfg.PaddleWebhookConfigured() && cfg.PaddlePriceMatrix[planCode]["czk"] != "" && cfg.PaddlePriceMatrix[planCode]["usd"] != ""
|
||||
}
|
||||
|
||||
func (cfg Config) StripeConfigured() bool {
|
||||
return strings.TrimSpace(cfg.StripeAPIKey) != ""
|
||||
}
|
||||
|
||||
func (cfg Config) StripeWebhookConfigured() bool {
|
||||
return strings.TrimSpace(cfg.StripeWebhookKey) != ""
|
||||
}
|
||||
|
||||
func (cfg Config) StripeCheckoutConfigured(planCode string) bool {
|
||||
planCode = shared.NormalizePlanCode(planCode)
|
||||
return cfg.StripeConfigured() && cfg.StripeWebhookConfigured() && cfg.StripePriceMatrix[planCode]["czk"] != "" && cfg.StripePriceMatrix[planCode]["usd"] != ""
|
||||
}
|
||||
|
||||
func (cfg Config) BillingProvider() string {
|
||||
if cfg.StripeConfigured() {
|
||||
return "stripe"
|
||||
}
|
||||
return "paddle"
|
||||
}
|
||||
|
||||
func (cfg Config) BillingConfigured() bool {
|
||||
return cfg.StripeConfigured() || cfg.PaddleConfigured()
|
||||
}
|
||||
|
||||
func (cfg Config) BillingWebhookConfigured() bool {
|
||||
return cfg.StripeWebhookConfigured() || cfg.PaddleWebhookConfigured()
|
||||
}
|
||||
|
||||
func paddlePriceMatrixFromEnv() map[string]map[string]string {
|
||||
matrix := map[string]map[string]string{
|
||||
"starter": {},
|
||||
@@ -132,6 +172,32 @@ func paddlePriceMatrixFromEnv() map[string]map[string]string {
|
||||
return matrix
|
||||
}
|
||||
|
||||
func stripePriceMatrixFromEnv() map[string]map[string]string {
|
||||
matrix := map[string]map[string]string{
|
||||
"starter": {},
|
||||
"pro": {},
|
||||
"business": {},
|
||||
}
|
||||
for _, planCode := range []string{"starter", "pro", "business"} {
|
||||
envPlan := strings.ToUpper(strings.ReplaceAll(planCode, "-", "_"))
|
||||
|
||||
// Monthly prices
|
||||
matrix[planCode][planCode+":czk:monthly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_MONTHLY_PRICE_ID"))
|
||||
matrix[planCode][planCode+":usd:monthly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_MONTHLY_PRICE_ID"))
|
||||
matrix[planCode][planCode+":czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_PRICE_ID"))
|
||||
matrix[planCode][planCode+":usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_PRICE_ID"))
|
||||
matrix[planCode]["czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_PRICE_ID"))
|
||||
matrix[planCode]["usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_PRICE_ID"))
|
||||
|
||||
// Yearly prices
|
||||
matrix[planCode][planCode+":czk:yearly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_YEARLY_PRICE_ID"))
|
||||
matrix[planCode][planCode+":usd:yearly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_YEARLY_PRICE_ID"))
|
||||
matrix[planCode]["yearly:czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_YEARLY_PRICE_ID"))
|
||||
matrix[planCode]["yearly:usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_YEARLY_PRICE_ID"))
|
||||
}
|
||||
return matrix
|
||||
}
|
||||
|
||||
func normalizePaddleEnvironment(value string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "live", "production":
|
||||
|
||||
Reference in New Issue
Block a user