mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-04 20:43:01 +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:
@@ -38,6 +38,23 @@ type Repository interface {
|
||||
UpdateTenantBillingState(ctx context.Context, tenantID string, planCode string, subscriptionStatus string, subscriptionID string) error
|
||||
RecordBillingEvent(ctx context.Context, tenantID string, provider string, eventID string, eventType string, payload []byte) (bool, error)
|
||||
|
||||
// Auth methods
|
||||
GetUserByEmail(ctx context.Context, email string) (*UserRecord, error)
|
||||
GetUserByID(ctx context.Context, userID string) (*UserRecord, error)
|
||||
CreateUser(ctx context.Context, email, passwordHash, name, provider, role string) (*UserRecord, error)
|
||||
UpdateLastLogin(ctx context.Context, userID string) error
|
||||
MarkEmailVerified(ctx context.Context, userID string) error
|
||||
CreateMagicLink(ctx context.Context, token, userID, email string, expiresAt time.Time) error
|
||||
GetMagicLink(ctx context.Context, token string) (*MagicLinkRecord, error)
|
||||
MarkMagicLinkUsed(ctx context.Context, token string) error
|
||||
|
||||
// Admin methods
|
||||
ListAllTenants(ctx context.Context, limit, offset int) ([]TenantRecord, int, error)
|
||||
ListAllUsers(ctx context.Context, limit, offset int) ([]UserRecord, int, error)
|
||||
GetPlatformStats(ctx context.Context) (PlatformStats, error)
|
||||
CreateAdminAuditLog(ctx context.Context, params AdminAuditLogParams) error
|
||||
UpdateUserRole(ctx context.Context, userID, role string) error
|
||||
|
||||
// Location / Zone Management
|
||||
ListLocationsByTenant(ctx context.Context, tenantID string) ([]LocationRecord, error)
|
||||
GetLocationByID(ctx context.Context, locationID string) (LocationRecord, error)
|
||||
@@ -85,6 +102,46 @@ type TenantRecord struct {
|
||||
BillingSubscription *string
|
||||
}
|
||||
|
||||
type UserRecord struct {
|
||||
ID uuid.UUID
|
||||
Email string
|
||||
Name *string
|
||||
PasswordHash *string
|
||||
EmailVerified bool
|
||||
Provider string
|
||||
Role string
|
||||
CreatedAt time.Time
|
||||
LastLoginAt *time.Time
|
||||
}
|
||||
|
||||
type MagicLinkRecord struct {
|
||||
Token string
|
||||
UserID uuid.UUID
|
||||
Email string
|
||||
Used bool
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type PlatformStats struct {
|
||||
TotalTenants int64 `json:"totalTenants"`
|
||||
TotalUsers int64 `json:"totalUsers"`
|
||||
ActiveSubscriptions int64 `json:"activeSubscriptions"`
|
||||
TrialSubscriptions int64 `json:"trialSubscriptions"`
|
||||
BookingsThisMonth int64 `json:"bookingsThisMonth"`
|
||||
RevenueThisMonth int64 `json:"revenueThisMonthCents"`
|
||||
}
|
||||
|
||||
type AdminAuditLogParams struct {
|
||||
AdminUserID string
|
||||
Action string
|
||||
ResourceType string
|
||||
ResourceID string
|
||||
Details map[string]any
|
||||
IPAddress string
|
||||
UserAgent string
|
||||
}
|
||||
|
||||
type TenantMembershipRecord struct {
|
||||
Tenant TenantRecord
|
||||
UserID string
|
||||
@@ -1303,6 +1360,60 @@ func (r *MemoryRepository) UpdateWorkingHours(_ context.Context, tenantID string
|
||||
return pgx.ErrNoRows
|
||||
}
|
||||
|
||||
// Auth methods for MemoryRepository
|
||||
func (r *MemoryRepository) GetUserByEmail(_ context.Context, email string) (*UserRecord, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) GetUserByID(_ context.Context, userID string) (*UserRecord, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) CreateUser(_ context.Context, email, passwordHash, name, provider, role string) (*UserRecord, error) {
|
||||
return &UserRecord{ID: uuid.New(), Email: email, Name: &name, Provider: provider, Role: role}, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) UpdateLastLogin(_ context.Context, userID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) MarkEmailVerified(_ context.Context, userID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) CreateMagicLink(_ context.Context, token, userID, email string, expiresAt time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) GetMagicLink(_ context.Context, token string) (*MagicLinkRecord, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) MarkMagicLinkUsed(_ context.Context, token string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Admin methods for MemoryRepository
|
||||
func (r *MemoryRepository) ListAllTenants(_ context.Context, limit, offset int) ([]TenantRecord, int, error) {
|
||||
return []TenantRecord{r.tenant}, 1, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) ListAllUsers(_ context.Context, limit, offset int) ([]UserRecord, int, error) {
|
||||
return []UserRecord{}, 0, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) GetPlatformStats(_ context.Context) (PlatformStats, error) {
|
||||
return PlatformStats{TotalTenants: 1, TotalUsers: 1, ActiveSubscriptions: 1}, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) CreateAdminAuditLog(_ context.Context, params AdminAuditLogParams) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) UpdateUserRole(_ context.Context, userID, role string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Reference(prefix string, at time.Time) string {
|
||||
return fmt.Sprintf("%s-%s-%s", prefix, at.UTC().Format("20060102150405"), strings.Split(uuid.NewString(), "-")[0])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user