fix: resolve im backend issue list regressions
This commit is contained in:
+73
-17
@@ -1359,6 +1359,7 @@ func createIMUserProductTables(tx *sql.Tx) error {
|
||||
profile_visibility VARCHAR(32) NOT NULL DEFAULT 'friends',
|
||||
last_seen_visibility VARCHAR(32) NOT NULL DEFAULT 'friends',
|
||||
read_receipts TINYINT NOT NULL DEFAULT 1,
|
||||
friend_request_verification TINYINT NOT NULL DEFAULT 1,
|
||||
group_invite_verification TINYINT NOT NULL DEFAULT 0,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
@@ -4762,31 +4763,44 @@ func (a *adapter) FileDeleteUnused(olderThan time.Time, limit int) ([]string, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var locations []string
|
||||
var ids []any
|
||||
type gcCandidate struct {
|
||||
id int
|
||||
location string
|
||||
}
|
||||
var candidates []gcCandidate
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var loc string
|
||||
if err = rows.Scan(&id, &loc); err != nil {
|
||||
break
|
||||
}
|
||||
candidates = append(candidates, gcCandidate{id: id, location: loc})
|
||||
}
|
||||
if err == nil {
|
||||
err = rows.Err()
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var locations []string
|
||||
var ids []any
|
||||
for _, candidate := range candidates {
|
||||
var noteRefs int
|
||||
fileID := store.EncodeUid(int64(id)).String()
|
||||
fileID := store.EncodeUid(int64(candidate.id)).String()
|
||||
if err = tx.QueryRow(`SELECT COUNT(*) FROM im_meeting_note_files WHERE file_id=? AND status=? LIMIT 1`, fileID, t.MeetingNoteFileStatusActive).Scan(¬eRefs); err != nil {
|
||||
break
|
||||
}
|
||||
if noteRefs > 0 {
|
||||
continue
|
||||
}
|
||||
if loc != "" {
|
||||
locations = append(locations, loc)
|
||||
if candidate.location != "" {
|
||||
locations = append(locations, candidate.location)
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
if err == nil {
|
||||
err = rows.Err()
|
||||
ids = append(ids, candidate.id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -4900,7 +4914,24 @@ func (a *adapter) FileUserHasAccess(fid string, uid t.Uid) (bool, error) {
|
||||
defer cancel()
|
||||
}
|
||||
var allowed int
|
||||
err := a.db.GetContext(ctx, &allowed, `
|
||||
buildQuery := func(includeRecorder bool) (string, []any) {
|
||||
recorderClause := ""
|
||||
if includeRecorder {
|
||||
recorderClause = `
|
||||
OR mtg.recorder_user_id=?`
|
||||
}
|
||||
args := []any{
|
||||
store.DecodeUid(fileID), fileID.String32(), "%/" + fileID.String32(), t.UploadCompleted,
|
||||
store.DecodeUid(uid), store.DecodeUid(uid), store.DecodeUid(uid), t.RelationshipFriend,
|
||||
store.DecodeUid(uid), store.DecodeUid(uid), t.GroupMemberStatusActive, store.DecodeUid(uid), store.DecodeUid(uid),
|
||||
store.DecodeUid(uid), fid, fid,
|
||||
fid, t.MeetingStateCanceled, store.DecodeUid(uid),
|
||||
}
|
||||
if includeRecorder {
|
||||
args = append(args, store.DecodeUid(uid))
|
||||
}
|
||||
args = append(args, store.DecodeUid(uid))
|
||||
return `
|
||||
SELECT COUNT(*) FROM im_fileuploads AS fu
|
||||
WHERE (fu.id=? OR fu.location=? OR fu.location LIKE ?) AND fu.status=? AND (
|
||||
fu.userid=?
|
||||
@@ -4958,18 +4989,29 @@ func (a *adapter) FileUserHasAccess(fid string, uid t.Uid) (bool, error) {
|
||||
AND mtg.end_at<=CURRENT_TIMESTAMP(3)
|
||||
AND (
|
||||
mtg.organizer_id=?
|
||||
OR mtg.recorder_user_id=?
|
||||
` + recorderClause + `
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM im_meeting_participants AS mp
|
||||
WHERE mp.meeting_id=mtg.meeting_id AND mp.user_id=?
|
||||
)
|
||||
)
|
||||
)
|
||||
) LIMIT 1`,
|
||||
store.DecodeUid(fileID), fileID.String32(), "%/"+fileID.String32(), t.UploadCompleted, store.DecodeUid(uid), store.DecodeUid(uid), store.DecodeUid(uid), t.RelationshipFriend,
|
||||
store.DecodeUid(uid), store.DecodeUid(uid), t.GroupMemberStatusActive, store.DecodeUid(uid), store.DecodeUid(uid),
|
||||
store.DecodeUid(uid), fid, fid,
|
||||
fid, t.MeetingStateCanceled, store.DecodeUid(uid), store.DecodeUid(uid), store.DecodeUid(uid))
|
||||
) LIMIT 1`, args
|
||||
}
|
||||
includeRecorder := true
|
||||
run := func() error {
|
||||
query, args := buildQuery(includeRecorder)
|
||||
return a.db.GetContext(ctx, &allowed, query, args...)
|
||||
}
|
||||
err := run()
|
||||
if isUnknownColumn(err) && strings.Contains(strings.ToLower(err.Error()), "recorder_user_id") {
|
||||
includeRecorder = false
|
||||
err = run()
|
||||
}
|
||||
for attempt := 0; isRetryableMySQLReadErr(err) && attempt < 2; attempt++ {
|
||||
time.Sleep(time.Duration(attempt+1) * 25 * time.Millisecond)
|
||||
err = run()
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -5108,6 +5150,20 @@ func isIDOutOfRange(err error) bool {
|
||||
return ok && myerr.Number == 1264 && strings.Contains(strings.ToLower(myerr.Message), "id")
|
||||
}
|
||||
|
||||
func isRetryableMySQLReadErr(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var myerr *ms.MySQLError
|
||||
if !errors.As(err, &myerr) {
|
||||
return false
|
||||
}
|
||||
if myerr.Number == 1213 || myerr.Number == 1205 {
|
||||
return true
|
||||
}
|
||||
return strings.EqualFold(string(myerr.SQLState[:]), "40001")
|
||||
}
|
||||
|
||||
func isPXCStrictModeNamedLockError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
|
||||
@@ -63,18 +63,6 @@ func ensureIMAdminGovernanceTables(exec sqlExecutor) error {
|
||||
PRIMARY KEY(id), UNIQUE INDEX uk_im_user_ip_whitelist(user_id,ip_cidr),
|
||||
INDEX idx_im_user_ip_whitelist_user(user_id,status), INDEX idx_im_user_ip_whitelist_deleted(deleted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`,
|
||||
`CREATE TABLE IF NOT EXISTS im_content_moderation_logs (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT, request_id VARCHAR(64) NOT NULL,
|
||||
scene VARCHAR(32) NOT NULL, topic VARCHAR(96) NOT NULL DEFAULT '', seqid BIGINT,
|
||||
actor_id BIGINT, provider VARCHAR(32) NOT NULL DEFAULT '', provider_request_id VARCHAR(96) NOT NULL DEFAULT '',
|
||||
risk_level VARCHAR(16) NOT NULL DEFAULT 'none', risk_labels JSON, action VARCHAR(16) NOT NULL,
|
||||
content_hash CHAR(64) NOT NULL, latency_ms BIGINT NOT NULL DEFAULT 0, error_code VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY(id),
|
||||
UNIQUE INDEX uk_im_content_moderation_request(request_id),
|
||||
INDEX idx_im_content_moderation_scene_time(scene,created_at DESC,id DESC),
|
||||
INDEX idx_im_content_moderation_actor_time(actor_id,created_at DESC,id DESC),
|
||||
INDEX idx_im_content_moderation_topic_seq(topic,seqid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`,
|
||||
}
|
||||
for _, statement := range statements {
|
||||
if _, err := exec.Exec(statement); err != nil {
|
||||
|
||||
@@ -6609,6 +6609,10 @@ func (a *adapter) TenantUserPrivacyUpdate(id t.TenantID, uid t.Uid, patch t.User
|
||||
sets = append(sets, "read_receipts=?")
|
||||
args = append(args, *patch.ReadReceipts)
|
||||
}
|
||||
if patch.FriendRequestVerification != nil {
|
||||
sets = append(sets, "friend_request_verification=?")
|
||||
args = append(args, *patch.FriendRequestVerification)
|
||||
}
|
||||
if patch.GroupInviteVerification != nil {
|
||||
sets = append(sets, "group_invite_verification=?")
|
||||
args = append(args, *patch.GroupInviteVerification)
|
||||
@@ -7180,30 +7184,32 @@ func (a *adapter) userProfileByUID(ctx context.Context, uid t.Uid) (*t.UserProfi
|
||||
|
||||
func (a *adapter) userPrivacyByUID(ctx context.Context, uid t.Uid) (*t.UserPrivacySettings, error) {
|
||||
row := struct {
|
||||
UserID int64 `db:"user_id"`
|
||||
AddMeMethods []byte `db:"add_me_methods"`
|
||||
ProfileVisibility string `db:"profile_visibility"`
|
||||
LastSeenVisibility string `db:"last_seen_visibility"`
|
||||
ReadReceipts bool `db:"read_receipts"`
|
||||
GroupInviteVerification bool `db:"group_invite_verification"`
|
||||
Version int
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
UserID int64 `db:"user_id"`
|
||||
AddMeMethods []byte `db:"add_me_methods"`
|
||||
ProfileVisibility string `db:"profile_visibility"`
|
||||
LastSeenVisibility string `db:"last_seen_visibility"`
|
||||
ReadReceipts bool `db:"read_receipts"`
|
||||
FriendRequestVerification bool `db:"friend_request_verification"`
|
||||
GroupInviteVerification bool `db:"group_invite_verification"`
|
||||
Version int
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}{}
|
||||
if err := sqlx.GetContext(ctx, a.db, &row, `SELECT user_id,add_me_methods,profile_visibility,last_seen_visibility,
|
||||
read_receipts,group_invite_verification,version,created_at,updated_at FROM im_user_privacy_settings WHERE user_id=?`, decodeUID(uid)); err != nil {
|
||||
read_receipts,friend_request_verification,group_invite_verification,version,created_at,updated_at FROM im_user_privacy_settings WHERE user_id=?`, decodeUID(uid)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
settings := &t.UserPrivacySettings{
|
||||
UserID: store.EncodeUid(row.UserID),
|
||||
AddMeMethods: map[string]bool{"account": true, "phone": true, "qr": true, "group": true},
|
||||
ProfileVisibility: row.ProfileVisibility,
|
||||
LastSeenVisibility: row.LastSeenVisibility,
|
||||
ReadReceipts: row.ReadReceipts,
|
||||
GroupInviteVerification: row.GroupInviteVerification,
|
||||
Version: row.Version,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
UserID: store.EncodeUid(row.UserID),
|
||||
AddMeMethods: map[string]bool{"account": true, "phone": true, "qr": true, "group": true},
|
||||
ProfileVisibility: row.ProfileVisibility,
|
||||
LastSeenVisibility: row.LastSeenVisibility,
|
||||
ReadReceipts: row.ReadReceipts,
|
||||
FriendRequestVerification: row.FriendRequestVerification,
|
||||
GroupInviteVerification: row.GroupInviteVerification,
|
||||
Version: row.Version,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}
|
||||
_ = json.Unmarshal(row.AddMeMethods, &settings.AddMeMethods)
|
||||
return settings, nil
|
||||
@@ -9314,7 +9320,7 @@ func (a *adapter) TenantSystemNotificationMarkRead(id t.TenantID, uid t.Uid, eve
|
||||
}
|
||||
res, err := a.db.ExecContext(ctx,
|
||||
`UPDATE im_system_notifications
|
||||
SET read_at=COALESCE(read_at,?),updated_at=?
|
||||
SET read_at=?,updated_at=?
|
||||
WHERE user_id=? AND event_id=?`,
|
||||
readAt, readAt, decodeUID(uid), eventID)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -60,7 +63,17 @@ func (a *adapter) TenantApplicationCreate(application *t.TenantApplication) (*t.
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := db.ExecContext(ctx, `INSERT INTO im_tenant_applications
|
||||
var existingID string
|
||||
err := db.QueryRowContext(ctx, `SELECT application_id FROM im_tenant_applications
|
||||
WHERE credit_code=? AND status IN ('received','pending','reviewing') ORDER BY id DESC LIMIT 1`,
|
||||
strings.ToUpper(strings.TrimSpace(application.CreditCode))).Scan(&existingID)
|
||||
if err == nil && existingID != "" {
|
||||
return nil, t.ErrDuplicate
|
||||
}
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
_, err = db.ExecContext(ctx, `INSERT INTO im_tenant_applications
|
||||
(application_id,credit_code,enterprise_email,license_file_id,tenant_code_hint,client_context,idempotency_key,status,estimated_review_hour,submitted_at,created_at,updated_at)
|
||||
VALUES(?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
application.ApplicationID, application.CreditCode, application.EnterpriseEmail, application.LicenseFileID,
|
||||
|
||||
@@ -20,8 +20,10 @@ const (
|
||||
groupSettingsSelectColumns = `topic,owner_user_id,name,avatar_file_id,description,group_status,join_approval,mute_all,
|
||||
member_upload_files,member_private_chat,member_invite,max_member_count,max_admin_count,member_count,version,
|
||||
created_by,dissolved_by,dissolved_at,created_at,updated_at`
|
||||
groupAnnouncementSelectColumns = `id,announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at`
|
||||
groupMuteSelectColumns = `topic,user_id,muted_by,muted_at,expires_at,reason,status,revoked_by,revoked_at,version,created_at,updated_at`
|
||||
groupAnnouncementSelectColumns = `id,announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at,
|
||||
(SELECT COUNT(*) FROM im_group_announcement_reads AS ar WHERE ar.topic=im_group_announcements.topic AND ar.announcement_id=im_group_announcements.announcement_id) AS read_count,
|
||||
(SELECT COUNT(*) FROM im_group_members AS gm WHERE gm.topic=im_group_announcements.topic AND gm.member_status='active') AS member_count`
|
||||
groupMuteSelectColumns = `topic,user_id,muted_by,muted_at,expires_at,reason,status,revoked_by,revoked_at,version,created_at,updated_at`
|
||||
)
|
||||
|
||||
func (a *adapter) TenantGroupSettingsCreate(id t.TenantID, settings *t.GroupSettings) (*t.GroupSettings, error) {
|
||||
|
||||
Reference in New Issue
Block a user