|
|
|
@@ -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",
|
|
|
|
|