fix: Enhance Kol Variable Rendering and Management
- Updated the regex for placeholder matching to support more flexible key formats. - Refactored variable storage to allow both ID and key lookups in rendering. - Improved schema validation to check for duplicate keys and enforce non-empty keys. - Added new utility functions for variable value lookup and display name generation. - Enhanced tests to cover new key-based variable scenarios. - Refactored KolPrompt repository to simplify prompt storage and management, including the removal of obsolete revision handling. - Introduced new API endpoints for saving, activating, and archiving prompts. - Added new utility functions for handling Kol placeholders and platform options in the admin web. - Implemented database migrations to simplify prompt storage structure and ensure data integrity.
This commit is contained in:
@@ -23,6 +23,8 @@ import (
|
||||
type seedState struct {
|
||||
TenantID int64
|
||||
UserID int64
|
||||
TestTenantID int64
|
||||
TestUserID int64
|
||||
PlanID int64
|
||||
BrandID int64
|
||||
KeywordPrimaryID int64
|
||||
@@ -129,6 +131,8 @@ func main() {
|
||||
fmt.Println("Seed data inserted successfully.")
|
||||
fmt.Printf(" Tenant ID: %d\n", state.TenantID)
|
||||
fmt.Printf(" User ID: %d (admin@geo.local / Admin@123)\n", state.UserID)
|
||||
fmt.Printf(" Test Tenant ID: %d\n", state.TestTenantID)
|
||||
fmt.Printf(" Test User ID: %d (test@geo.local / Test@123)\n", state.TestUserID)
|
||||
fmt.Printf(" Plan ID: %d (free)\n", state.PlanID)
|
||||
fmt.Printf(" Brand ID: %d (轻氧口腔)\n", state.BrandID)
|
||||
fmt.Printf(" Plugin Installation ID: %d\n", state.PluginInstallationID)
|
||||
@@ -176,6 +180,10 @@ func seedBusinessData(ctx context.Context, pool *pgxpool.Pool) (*seedState, erro
|
||||
if err := ensureTemplates(ctx, tx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.TestTenantID, state.TestUserID, err = ensureSecondaryTenantUser(ctx, tx, state.PlanID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state.BrandID, err = ensureBrand(ctx, tx, state.TenantID)
|
||||
if err != nil {
|
||||
@@ -280,48 +288,89 @@ func seedMonitoringData(ctx context.Context, pool *pgxpool.Pool, state *seedStat
|
||||
}
|
||||
|
||||
func ensureTenant(ctx context.Context, tx pgx.Tx) (int64, error) {
|
||||
return ensureNamedTenant(ctx, tx, "GEO Demo")
|
||||
}
|
||||
|
||||
func ensureNamedTenant(ctx context.Context, tx pgx.Tx, name string) (int64, error) {
|
||||
var tenantID int64
|
||||
err := tx.QueryRow(ctx, `
|
||||
WITH ensured AS (
|
||||
INSERT INTO tenants (name, status)
|
||||
VALUES ('GEO Demo', 'active')
|
||||
VALUES ($1, 'active')
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING id
|
||||
)
|
||||
SELECT id FROM ensured
|
||||
UNION ALL
|
||||
SELECT id FROM tenants WHERE name = 'GEO Demo'
|
||||
SELECT id FROM tenants WHERE name = $1
|
||||
LIMIT 1
|
||||
`).Scan(&tenantID)
|
||||
`, name).Scan(&tenantID)
|
||||
return tenantID, wrapSeedErr("insert tenant", err)
|
||||
}
|
||||
|
||||
func ensureUser(ctx context.Context, tx pgx.Tx, passwordHash string) (int64, error) {
|
||||
return ensureNamedUser(ctx, tx, "admin@geo.local", "Admin", passwordHash)
|
||||
}
|
||||
|
||||
func ensureNamedUser(ctx context.Context, tx pgx.Tx, email, name, passwordHash string) (int64, error) {
|
||||
var userID int64
|
||||
err := tx.QueryRow(ctx, `
|
||||
WITH ensured AS (
|
||||
INSERT INTO users (email, password_hash, name, status)
|
||||
VALUES ('admin@geo.local', $1, 'Admin', 'active')
|
||||
VALUES ($1, $2, $3, 'active')
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING id
|
||||
)
|
||||
SELECT id FROM ensured
|
||||
UNION ALL
|
||||
SELECT id FROM users WHERE email = 'admin@geo.local'
|
||||
SELECT id FROM users WHERE email = $1
|
||||
LIMIT 1
|
||||
`, passwordHash).Scan(&userID)
|
||||
`, email, passwordHash, name).Scan(&userID)
|
||||
return userID, wrapSeedErr("insert user", err)
|
||||
}
|
||||
|
||||
func ensureMembership(ctx context.Context, tx pgx.Tx, tenantID, userID int64) error {
|
||||
return ensureMembershipWithRole(ctx, tx, tenantID, userID, "tenant_admin")
|
||||
}
|
||||
|
||||
func ensureMembershipWithRole(ctx context.Context, tx pgx.Tx, tenantID, userID int64, role string) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
INSERT INTO tenant_memberships (tenant_id, user_id, tenant_role)
|
||||
VALUES ($1, $2, 'tenant_admin')
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT DO NOTHING
|
||||
`, tenantID, userID)
|
||||
`, tenantID, userID, role)
|
||||
return wrapSeedErr("insert membership", err)
|
||||
}
|
||||
|
||||
func ensureSecondaryTenantUser(ctx context.Context, tx pgx.Tx, planID int64) (int64, int64, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte("Test@123"), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("hash secondary user password: %w", err)
|
||||
}
|
||||
|
||||
tenantID, err := ensureNamedTenant(ctx, tx, "test")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
userID, err := ensureNamedUser(ctx, tx, "test@geo.local", "test", string(hash))
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if err := ensureMembershipWithRole(ctx, tx, tenantID, userID, "tenant_admin"); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if err := ensureSubscription(ctx, tx, tenantID, planID); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if err := ensureQuotaLedger(ctx, tx, tenantID); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return tenantID, userID, nil
|
||||
}
|
||||
|
||||
func ensurePlan(ctx context.Context, tx pgx.Tx) (int64, error) {
|
||||
var planID int64
|
||||
err := tx.QueryRow(ctx, `
|
||||
|
||||
Reference in New Issue
Block a user