fix im server shared database config

This commit is contained in:
richjohnrambo
2026-09-22 16:45:24 +08:00
parent b8c93e8d6a
commit e4416da04a
11 changed files with 114 additions and 140 deletions
-4
View File
@@ -51,8 +51,6 @@ type adminInternalTenantDTO struct {
Code string `json:"code"`
Name string `json:"name"`
TenantDesc *string `json:"tenant_desc,omitempty"`
DBName string `json:"db_name"`
DBDSN string `json:"db_dsn"`
State types.TenantState `json:"state"`
CreatedAt time.Time `json:"created_at"`
CreatedBy int64 `json:"created_by"`
@@ -422,8 +420,6 @@ func (t adminInternalTenantDTO) toStoreTenant() *types.Tenant {
Code: strings.TrimSpace(t.Code),
Name: t.Name,
TenantDesc: t.TenantDesc,
DBName: t.DBName,
DBDSN: t.DBDSN,
State: t.State,
CreatedAt: t.CreatedAt,
CreatedBy: t.CreatedBy,
+37 -81
View File
@@ -56,8 +56,8 @@ const (
adpVersion = 123
adapterName = "mysql"
defaultDSN = "root:@tcp(localhost:3306)/tinode?parseTime=true"
defaultDatabase = "tinode"
defaultDSN = "root:@tcp(localhost:3306)/meeting_im?parseTime=true"
defaultDatabase = "meeting_im"
defaultMaxResults = 1024
// This is capped by the Session's send queue limit (128).
@@ -421,17 +421,11 @@ func (a *adapter) TenantGetByCode(code string) (*t.Tenant, error) {
db = a.registryDB
}
err := db.GetContext(ctx, &tenant,
`SELECT id, code, name, tenant_desc, IFNULL(db_name,'') AS db_name, IFNULL(db_dsn,'') AS db_dsn,
state, created_at, created_by, updated_at, updated_by
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE code=? LIMIT 1`, code)
if err == sql.ErrNoRows {
return nil, t.ErrNotFound
}
if isUnknownColumn(err) {
err = db.GetContext(ctx, &tenant,
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE code=? LIMIT 1`, code)
}
if err != nil {
return nil, err
}
@@ -460,14 +454,8 @@ func (a *adapter) TenantListActive(limit int) ([]t.Tenant, error) {
}
var tenants []t.Tenant
err := db.SelectContext(ctx, &tenants,
`SELECT id, code, name, tenant_desc, IFNULL(db_name,'') AS db_name, IFNULL(db_dsn,'') AS db_dsn,
state, created_at, created_by, updated_at, updated_by
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE state=? ORDER BY id ASC LIMIT ?`, t.TenantStateActive, limit)
if isUnknownColumn(err) {
err = db.SelectContext(ctx, &tenants,
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE state=? ORDER BY id ASC LIMIT ?`, t.TenantStateActive, limit)
}
return tenants, err
}
@@ -518,8 +506,8 @@ func (a *adapter) TenantAdapterFor(tenantID t.TenantID) (dbadapter.TenantBusines
baseConfig: a.baseConfig,
tenantDBRouting: false,
tenantID: tenantID,
// A dedicated database is still tenant-owned. Keep tenant predicates and
// explicit tenant_id writes enabled even though databases are not shared.
// Tenant adapters share the same database. Keep tenant predicates and
// explicit tenant_id writes enabled for logical isolation.
tenantScopedDB: false,
}
a.tenantMu.Lock()
@@ -543,17 +531,11 @@ func (a *adapter) tenantByID(tenantID t.TenantID) (*t.Tenant, error) {
}
var tenant t.Tenant
err := db.GetContext(ctx, &tenant,
`SELECT id, code, name, tenant_desc, IFNULL(db_name,'') AS db_name, IFNULL(db_dsn,'') AS db_dsn,
state, created_at, created_by, updated_at, updated_by
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE id=? LIMIT 1`, tenantID)
if err == sql.ErrNoRows {
return nil, t.ErrNotFound
}
if isUnknownColumn(err) {
err = db.GetContext(ctx, &tenant,
`SELECT id, code, name, tenant_desc, state, created_at, created_by, updated_at, updated_by
FROM im_tenant WHERE id=? LIMIT 1`, tenantID)
}
if err != nil {
return nil, err
}
@@ -563,7 +545,7 @@ func (a *adapter) tenantByID(tenantID t.TenantID) (*t.Tenant, error) {
return &tenant, nil
}
func createTenantRegistry(tx *sql.Tx, registryDB, businessDB string) error {
func createTenantRegistry(tx *sql.Tx, registryDB, _ string) error {
if _, err := tx.Exec("CREATE DATABASE IF NOT EXISTS " + quoteIdent(registryDB) + " CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci"); err != nil {
return err
}
@@ -572,52 +554,40 @@ func createTenantRegistry(tx *sql.Tx, registryDB, businessDB string) error {
}
if _, err := tx.Exec(
`CREATE TABLE IF NOT EXISTS im_tenant(
id BIGINT NOT NULL,
code VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
tenant_desc VARCHAR(256),
db_name VARCHAR(128) NOT NULL DEFAULT '',
db_dsn VARCHAR(1024) NOT NULL DEFAULT '',
state SMALLINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
created_by BIGINT NOT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
updated_by BIGINT NOT NULL,
id BIGINT NOT NULL,
code VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
tenant_desc VARCHAR(256),
max_users BIGINT UNSIGNED NOT NULL DEFAULT 0,
max_groups BIGINT UNSIGNED NOT NULL DEFAULT 0,
max_group_members INT UNSIGNED NOT NULL DEFAULT 0,
realname_review_policy TINYINT(1) NOT NULL DEFAULT 0,
realname_provider_mode VARCHAR(32) NOT NULL DEFAULT 'id_card_2factor',
realname_required TINYINT(1) NOT NULL DEFAULT 0,
realname_allow_skip TINYINT(1) NOT NULL DEFAULT 1,
realname_remind_after VARCHAR(32) NOT NULL DEFAULT '24h',
aliyun_moderation_enabled TINYINT(1) NOT NULL DEFAULT 0,
local_moderation_enabled TINYINT(1) NOT NULL DEFAULT 0,
message_encryption_enabled TINYINT(1) NOT NULL DEFAULT 0,
default_policy_group_id BIGINT,
security_policy_version INT UNSIGNED NOT NULL DEFAULT 0,
global_friend_enabled TINYINT(1) NOT NULL DEFAULT 1,
message_curfew_enabled TINYINT(1) NOT NULL DEFAULT 0,
state SMALLINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
created_by BIGINT NOT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
updated_by BIGINT NOT NULL,
PRIMARY KEY(id),
UNIQUE INDEX uk_im_tenant_code(code),
INDEX idx_im_tenant_db_name(db_name)
)`); err != nil {
return err
}
if _, err := tx.Exec(
`CREATE TABLE IF NOT EXISTS im_tenant_config(
id BIGINT NOT NULL AUTO_INCREMENT,
tenant_id BIGINT NOT NULL,
max_users BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0 means unlimited',
max_groups BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0 means unlimited',
max_group_members INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0 means unlimited',
realname_review_policy TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Real-name review policy: 0 none, 1 auto then manual',
realname_provider_mode VARCHAR(32) NOT NULL DEFAULT 'id_card_2factor' COMMENT 'id_card_2factor or smart_verify',
realname_required TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Require real-name verification',
realname_allow_skip TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Allow skipping real-name reminder',
realname_remind_after VARCHAR(32) NOT NULL DEFAULT '24h' COMMENT 'Real-name reminder interval',
aliyun_moderation_enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Enable Aliyun content moderation',
local_moderation_enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Enable local content moderation',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
created_by BIGINT NOT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
updated_by BIGINT NOT NULL,
PRIMARY KEY(id),
UNIQUE INDEX uk_im_tenant_config_tenant_id(tenant_id),
CONSTRAINT fk_im_tenant_config_tenant FOREIGN KEY(tenant_id) REFERENCES im_tenant(id) ON DELETE CASCADE
UNIQUE INDEX uk_im_tenant_code(code)
)`); err != nil {
return err
}
_, err := tx.Exec(
`INSERT INTO im_tenant(id,code,name,tenant_desc,db_name,db_dsn,state,created_by,updated_by)
VALUES(1,'default','Default Tenant',NULL,?,'',?,0,0)
ON DUPLICATE KEY UPDATE db_name=VALUES(db_name), state=VALUES(state), updated_by=VALUES(updated_by)`,
businessDB, t.TenantStateActive)
`INSERT INTO im_tenant(id,code,name,tenant_desc,state,created_by,updated_by)
VALUES(1,'default','Default Tenant',NULL,?,0,0)
ON DUPLICATE KEY UPDATE state=VALUES(state), updated_by=VALUES(updated_by)`,
t.TenantStateActive)
return err
}
@@ -1007,20 +977,6 @@ func createIMUserProductTables(tx *sql.Tx) error {
return nil
}
func ensureTenantRoutingColumns(a *adapter) error {
statements := []string{
"ALTER TABLE im_tenant ADD COLUMN db_name VARCHAR(128) NOT NULL DEFAULT '' AFTER tenant_desc",
"ALTER TABLE im_tenant ADD COLUMN db_dsn VARCHAR(1024) NOT NULL DEFAULT '' AFTER db_name",
"UPDATE im_tenant SET db_name='tinode' WHERE code='default' AND db_name=''",
}
for _, stmt := range statements {
if _, err := a.db.Exec(stmt); err != nil && !isDuplicateColumn(err) {
return err
}
}
return nil
}
func upgradeIMMessageActions(a *adapter) error {
statements := []string{
"ALTER TABLE im_messages ADD COLUMN version INT NOT NULL DEFAULT 1 AFTER client_msg_id",
+2 -2
View File
@@ -26,9 +26,9 @@ func (a *adapter) RealNamePolicy(tenantID t.TenantID) (*t.RealNamePolicy, error)
defer cancel()
}
var policy t.RealNamePolicy
err := db.GetContext(ctx, &policy, `SELECT tenant_id, realname_review_policy, realname_provider_mode,
err := db.GetContext(ctx, &policy, `SELECT id AS tenant_id, realname_review_policy, realname_provider_mode,
realname_required, realname_allow_skip, realname_remind_after
FROM im_tenant_config WHERE tenant_id=? LIMIT 1`, tenantID)
FROM im_tenant WHERE id=? LIMIT 1`, tenantID)
if errors.Is(err, sql.ErrNoRows) {
policy = *defaultRealNamePolicy(tenantID)
return &policy, nil
+3 -4
View File
@@ -6,16 +6,15 @@
# Current MySQL initialization is generated by server/db/mysql/adapter.go:
#
# - Control database: hotgo
# - im_tenant
# - im_tenant_config
# - im_tenant (includes tenant feature and policy configuration)
# - im_public_uploads, im_tenant_applications and im_tenant_entry_feedbacks
# for unauthenticated enterprise registration and tenant-entry feedback
#
# - Default tenant business database: tinode
# - Shared tenant business database: meeting_im
# - im_users, im_topics, im_subscriptions, im_messages and extension tables
# - im_http_idempotency for HTTP write idempotency
# - im_file_review_tasks for asynchronous content safety and malware checks
# - business tables do not store tenant_id in independent-database mode
# - business tables store tenant_id; high-volume tables are physically partitioned by tenant
#
# The previous shared-table tenant_id draft has been removed from this file to
# avoid accidental execution. If standalone SQL initialization scripts are
+3 -3
View File
@@ -263,13 +263,13 @@ func TestTenantAuthIsolation(t *testing.T) {
}
}
}
createTenant := func(code, dbName string) types.TenantID {
createTenant := func(code, _ string) types.TenantID {
var id int64
if err := db.Get(&id, "SELECT COALESCE(MAX(id),0)+1 FROM im_tenant"); err != nil {
t.Fatal(err)
}
_, err := db.Exec("INSERT INTO im_tenant(id,code,name,db_name,state,created_by,updated_by) VALUES(?,?,?,?,?,?,?)",
id, code+suffix, code, dbName, types.TenantStateActive, 0, 0)
_, err := db.Exec("INSERT INTO im_tenant(id,code,name,state,created_by,updated_by) VALUES(?,?,?,?,?,?)",
id, code+suffix, code, types.TenantStateActive, 0, 0)
if err != nil {
t.Fatal(err)
}
-2
View File
@@ -35,8 +35,6 @@ type Tenant struct {
Code string `db:"code" json:"code"`
Name string `db:"name" json:"name"`
TenantDesc *string `db:"tenant_desc" json:"tenant_desc,omitempty"`
DBName string `db:"db_name" json:"-"`
DBDSN string `db:"db_dsn" json:"-"`
State TenantState `db:"state" json:"-"`
CreatedAt time.Time `db:"created_at" json:"-"`
CreatedBy int64 `db:"created_by" json:"-"`