mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
242 lines
7.9 KiB
Go
242 lines
7.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
type PlanRepository interface {
|
|
Create(ctx context.Context, plan *models.Plan) error
|
|
Update(ctx context.Context, plan *models.Plan) error
|
|
GetByID(ctx context.Context, id uuid.UUID) (*models.Plan, error)
|
|
GetByStripePriceID(ctx context.Context, priceID string) (*models.Plan, error)
|
|
GetByStripeProductID(ctx context.Context, productID string) (*models.Plan, error)
|
|
List(ctx context.Context, publicOnly bool) ([]*models.Plan, error)
|
|
|
|
// Rate limits
|
|
GetRateLimits(ctx context.Context, planID uuid.UUID) (*models.PlanRateLimits, error)
|
|
SetRateLimits(ctx context.Context, limits *models.PlanRateLimits) error
|
|
}
|
|
|
|
type planRepository struct {
|
|
db *pgxpool.Pool
|
|
}
|
|
|
|
func NewPlanRepository(db *pgxpool.Pool) PlanRepository {
|
|
return &planRepository{db: db}
|
|
}
|
|
|
|
func (r *planRepository) Create(ctx context.Context, plan *models.Plan) error {
|
|
query := `
|
|
INSERT INTO plans (
|
|
id, name, max_contacts, daily_emails, ai_generation, account_limit,
|
|
price, discounted_price, duration_id, savings, public,
|
|
stripe_price_id, stripe_product_id, created_at, updated_at
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8,
|
|
(SELECT id FROM durations WHERE title = $9),
|
|
$10, $11, $12, $13, $14, $15
|
|
)
|
|
`
|
|
|
|
now := time.Now()
|
|
_, err := r.db.Exec(ctx, query,
|
|
plan.ID, plan.Name, plan.MaxContacts, plan.DailyEmails, plan.AIGeneration, plan.AccountLimit,
|
|
plan.Price, plan.DiscountedPrice, string(plan.Duration), plan.Savings, plan.Public,
|
|
plan.StripePriceID, plan.StripeProductID, now, now,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (r *planRepository) Update(ctx context.Context, plan *models.Plan) error {
|
|
query := `
|
|
UPDATE plans SET
|
|
name = $2,
|
|
max_contacts = $3,
|
|
daily_emails = $4,
|
|
ai_generation = $5,
|
|
account_limit = $6,
|
|
price = $7,
|
|
discounted_price = $8,
|
|
savings = $9,
|
|
public = $10,
|
|
stripe_price_id = $11,
|
|
stripe_product_id = $12,
|
|
updated_at = $13
|
|
WHERE id = $1
|
|
`
|
|
|
|
_, err := r.db.Exec(ctx, query,
|
|
plan.ID, plan.Name, plan.MaxContacts, plan.DailyEmails, plan.AIGeneration, plan.AccountLimit,
|
|
plan.Price, plan.DiscountedPrice, plan.Savings, plan.Public,
|
|
plan.StripePriceID, plan.StripeProductID, time.Now(),
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (r *planRepository) GetByID(ctx context.Context, id uuid.UUID) (*models.Plan, error) {
|
|
return r.scanPlan(ctx, `
|
|
SELECT p.id, p.name, p.max_contacts, p.daily_emails, p.ai_generation, p.account_limit,
|
|
p.price, p.discounted_price, d.title, p.savings, p.public,
|
|
p.stripe_price_id, p.stripe_product_id, p.dedicated_workers, p.daily_campaign_limit,
|
|
p.created_at, p.updated_at
|
|
FROM plans p
|
|
LEFT JOIN durations d ON d.id = p.duration_id
|
|
WHERE p.id = $1
|
|
`, id)
|
|
}
|
|
|
|
func (r *planRepository) GetByStripePriceID(ctx context.Context, priceID string) (*models.Plan, error) {
|
|
return r.scanPlan(ctx, `
|
|
SELECT p.id, p.name, p.max_contacts, p.daily_emails, p.ai_generation, p.account_limit,
|
|
p.price, p.discounted_price, d.title, p.savings, p.public,
|
|
p.stripe_price_id, p.stripe_product_id, p.dedicated_workers, p.daily_campaign_limit,
|
|
p.created_at, p.updated_at
|
|
FROM plans p
|
|
LEFT JOIN durations d ON d.id = p.duration_id
|
|
WHERE p.stripe_price_id = $1
|
|
`, priceID)
|
|
}
|
|
|
|
func (r *planRepository) GetByStripeProductID(ctx context.Context, productID string) (*models.Plan, error) {
|
|
return r.scanPlan(ctx, `
|
|
SELECT p.id, p.name, p.max_contacts, p.daily_emails, p.ai_generation, p.account_limit,
|
|
p.price, p.discounted_price, d.title, p.savings, p.public,
|
|
p.stripe_price_id, p.stripe_product_id, p.dedicated_workers, p.daily_campaign_limit,
|
|
p.created_at, p.updated_at
|
|
FROM plans p
|
|
LEFT JOIN durations d ON d.id = p.duration_id
|
|
WHERE p.stripe_product_id = $1
|
|
`, productID)
|
|
}
|
|
|
|
func (r *planRepository) scanPlan(ctx context.Context, query string, args ...interface{}) (*models.Plan, error) {
|
|
row := r.db.QueryRow(ctx, query, args...)
|
|
|
|
var plan models.Plan
|
|
var duration *string
|
|
err := row.Scan(
|
|
&plan.ID, &plan.Name, &plan.MaxContacts, &plan.DailyEmails, &plan.AIGeneration, &plan.AccountLimit,
|
|
&plan.Price, &plan.DiscountedPrice, &duration, &plan.Savings, &plan.Public,
|
|
&plan.StripePriceID, &plan.StripeProductID, &plan.DedicatedWorkers, &plan.DailyCampaignLimit,
|
|
&plan.CreatedAt, &plan.UpdatedAt,
|
|
)
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if duration != nil {
|
|
plan.Duration = models.Duration(*duration)
|
|
}
|
|
return &plan, nil
|
|
}
|
|
|
|
func (r *planRepository) List(ctx context.Context, publicOnly bool) ([]*models.Plan, error) {
|
|
query := `
|
|
SELECT p.id, p.name, p.max_contacts, p.daily_emails, p.ai_generation, p.account_limit,
|
|
p.price, p.discounted_price, d.title, p.savings, p.public,
|
|
p.stripe_price_id, p.stripe_product_id, p.dedicated_workers, p.daily_campaign_limit,
|
|
p.created_at, p.updated_at
|
|
FROM plans p
|
|
LEFT JOIN durations d ON d.id = p.duration_id
|
|
`
|
|
if publicOnly {
|
|
query += " WHERE p.public = true"
|
|
}
|
|
query += " ORDER BY p.price ASC"
|
|
|
|
rows, err := r.db.Query(ctx, query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var plans []*models.Plan
|
|
for rows.Next() {
|
|
var plan models.Plan
|
|
var duration *string
|
|
err := rows.Scan(
|
|
&plan.ID, &plan.Name, &plan.MaxContacts, &plan.DailyEmails, &plan.AIGeneration, &plan.AccountLimit,
|
|
&plan.Price, &plan.DiscountedPrice, &duration, &plan.Savings, &plan.Public,
|
|
&plan.StripePriceID, &plan.StripeProductID, &plan.DedicatedWorkers, &plan.DailyCampaignLimit,
|
|
&plan.CreatedAt, &plan.UpdatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if duration != nil {
|
|
plan.Duration = models.Duration(*duration)
|
|
}
|
|
plans = append(plans, &plan)
|
|
}
|
|
return plans, nil
|
|
}
|
|
|
|
func (r *planRepository) GetRateLimits(ctx context.Context, planID uuid.UUID) (*models.PlanRateLimits, error) {
|
|
query := `SELECT * FROM plan_rate_limits WHERE plan_id = $1`
|
|
row := r.db.QueryRow(ctx, query, planID)
|
|
|
|
var limits models.PlanRateLimits
|
|
err := row.Scan(
|
|
&limits.PlanID,
|
|
&limits.LimitReadPM, &limits.LimitWritePM, &limits.LimitBulkPM,
|
|
&limits.LimitUniboxPM, &limits.LimitAnalyticsPM,
|
|
&limits.LimitAPICallsDaily, &limits.LimitBulkOpsDaily,
|
|
&limits.LimitWSMessagePM, &limits.LimitWSJoinPM, &limits.LimitWSEventPM, &limits.MaxConnections,
|
|
&limits.BurstMultiplier,
|
|
&limits.CreatedAt, &limits.UpdatedAt,
|
|
)
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &limits, nil
|
|
}
|
|
|
|
func (r *planRepository) SetRateLimits(ctx context.Context, limits *models.PlanRateLimits) error {
|
|
query := `
|
|
INSERT INTO plan_rate_limits (
|
|
plan_id, limit_read_pm, limit_write_pm, limit_bulk_pm,
|
|
limit_unibox_pm, limit_analytics_pm, limit_api_calls_daily, limit_bulk_ops_daily,
|
|
limit_ws_message_pm, limit_ws_join_pm, limit_ws_event_pm, max_connections,
|
|
burst_multiplier, created_at, updated_at
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
|
|
)
|
|
ON CONFLICT (plan_id) DO UPDATE SET
|
|
limit_read_pm = EXCLUDED.limit_read_pm,
|
|
limit_write_pm = EXCLUDED.limit_write_pm,
|
|
limit_bulk_pm = EXCLUDED.limit_bulk_pm,
|
|
limit_unibox_pm = EXCLUDED.limit_unibox_pm,
|
|
limit_analytics_pm = EXCLUDED.limit_analytics_pm,
|
|
limit_api_calls_daily = EXCLUDED.limit_api_calls_daily,
|
|
limit_bulk_ops_daily = EXCLUDED.limit_bulk_ops_daily,
|
|
limit_ws_message_pm = EXCLUDED.limit_ws_message_pm,
|
|
limit_ws_join_pm = EXCLUDED.limit_ws_join_pm,
|
|
limit_ws_event_pm = EXCLUDED.limit_ws_event_pm,
|
|
max_connections = EXCLUDED.max_connections,
|
|
burst_multiplier = EXCLUDED.burst_multiplier,
|
|
updated_at = EXCLUDED.updated_at
|
|
`
|
|
|
|
now := time.Now()
|
|
_, err := r.db.Exec(ctx, query,
|
|
limits.PlanID,
|
|
limits.LimitReadPM, limits.LimitWritePM, limits.LimitBulkPM,
|
|
limits.LimitUniboxPM, limits.LimitAnalyticsPM, limits.LimitAPICallsDaily, limits.LimitBulkOpsDaily,
|
|
limits.LimitWSMessagePM, limits.LimitWSJoinPM, limits.LimitWSEventPM, limits.MaxConnections,
|
|
limits.BurstMultiplier, now, now,
|
|
)
|
|
return err
|
|
}
|