Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
547 lines
16 KiB
Go
547 lines
16 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package sqlstore
|
|
|
|
import (
|
|
sq "github.com/mattermost/squirrel"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
)
|
|
|
|
type relationalCheckConfig struct {
|
|
parentName string
|
|
parentIdAttr string
|
|
childName string
|
|
childIdAttr string
|
|
canParentIdBeEmpty bool
|
|
sortRecords bool
|
|
filter any
|
|
}
|
|
|
|
func getOrphanedRecords(ss *SqlStore, cfg relationalCheckConfig) ([]model.OrphanedRecord, error) {
|
|
records := []model.OrphanedRecord{}
|
|
|
|
sub := ss.getQueryBuilder().
|
|
Select("TRUE").
|
|
From(cfg.parentName + " AS PT").
|
|
Prefix("NOT EXISTS (").
|
|
Suffix(")").
|
|
Where("PT.id = CT." + cfg.parentIdAttr)
|
|
|
|
main := ss.getQueryBuilder().
|
|
Select().
|
|
Column("CT." + cfg.parentIdAttr + " AS ParentId").
|
|
From(cfg.childName + " AS CT").
|
|
Where(sub)
|
|
|
|
if cfg.childIdAttr != "" {
|
|
main = main.Column("CT." + cfg.childIdAttr + " AS ChildId")
|
|
}
|
|
|
|
if cfg.canParentIdBeEmpty {
|
|
main = main.Where(sq.NotEq{"CT." + cfg.parentIdAttr: ""})
|
|
}
|
|
|
|
if cfg.filter != nil {
|
|
main = main.Where(cfg.filter)
|
|
}
|
|
|
|
if cfg.sortRecords {
|
|
main = main.OrderBy("CT." + cfg.parentIdAttr)
|
|
}
|
|
|
|
query, args, err := main.ToSql()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = ss.GetMaster().Select(&records, query, args...)
|
|
return records, err
|
|
}
|
|
|
|
func checkParentChildIntegrity(ss *SqlStore, config relationalCheckConfig) model.IntegrityCheckResult {
|
|
var result model.IntegrityCheckResult
|
|
var data model.RelationalIntegrityCheckData
|
|
|
|
config.sortRecords = true
|
|
data.Records, result.Err = getOrphanedRecords(ss, config)
|
|
if result.Err != nil {
|
|
mlog.Error("Error while getting orphaned records", mlog.Err(result.Err))
|
|
return result
|
|
}
|
|
data.ParentName = config.parentName
|
|
data.ChildName = config.childName
|
|
data.ParentIdAttr = config.parentIdAttr
|
|
data.ChildIdAttr = config.childIdAttr
|
|
result.Data = data
|
|
|
|
return result
|
|
}
|
|
|
|
func checkChannelsCommandWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "CommandWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkChannelsChannelMemberHistoryIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "ChannelMemberHistory",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkChannelsChannelMembersIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "ChannelMembers",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkChannelsIncomingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "IncomingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkChannelsOutgoingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "OutgoingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkChannelsPostsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "Posts",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkChannelsFileInfoIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Channels",
|
|
parentIdAttr: "ChannelId",
|
|
childName: "FileInfo",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkCommandsCommandWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Commands",
|
|
parentIdAttr: "CommandId",
|
|
childName: "CommandWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkPostsFileInfoIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Posts",
|
|
parentIdAttr: "PostId",
|
|
childName: "FileInfo",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkPostsPostsRootIdIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Posts",
|
|
parentIdAttr: "RootId",
|
|
childName: "Posts",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkPostsReactionsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Posts",
|
|
parentIdAttr: "PostId",
|
|
childName: "Reactions",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkSchemesChannelsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Schemes",
|
|
parentIdAttr: "SchemeId",
|
|
childName: "Channels",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkSchemesTeamsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Schemes",
|
|
parentIdAttr: "SchemeId",
|
|
childName: "Teams",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkSessionsAuditsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Sessions",
|
|
parentIdAttr: "SessionId",
|
|
childName: "Audits",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkTeamsChannelsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
res1 := checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "Channels",
|
|
childIdAttr: "Id",
|
|
filter: sq.NotEq{"CT.Type": []model.ChannelType{model.ChannelTypeDirect, model.ChannelTypeGroup}},
|
|
})
|
|
res2 := checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "Channels",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
filter: sq.Eq{"CT.Type": []model.ChannelType{model.ChannelTypeDirect, model.ChannelTypeGroup}},
|
|
})
|
|
data1 := res1.Data.(model.RelationalIntegrityCheckData)
|
|
data2 := res2.Data.(model.RelationalIntegrityCheckData)
|
|
data1.Records = append(data1.Records, data2.Records...)
|
|
res1.Data = data1
|
|
return res1
|
|
}
|
|
|
|
func checkTeamsCommandsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "Commands",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkTeamsIncomingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "IncomingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkTeamsOutgoingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "OutgoingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkTeamsTeamMembersIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "TeamId",
|
|
childName: "TeamMembers",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersAuditsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Audits",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkUsersCommandWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "CommandWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersChannelMemberHistoryIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "ChannelMemberHistory",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersChannelMembersIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "ChannelMembers",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersChannelsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "Channels",
|
|
childIdAttr: "Id",
|
|
canParentIdBeEmpty: true,
|
|
})
|
|
}
|
|
|
|
func checkUsersCommandsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "Commands",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersCompliancesIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Compliances",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersEmojiIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "Emoji",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersFileInfoIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "FileInfo",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersIncomingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "IncomingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersOAuthAccessDataIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "OAuthAccessData",
|
|
childIdAttr: "Token",
|
|
})
|
|
}
|
|
|
|
func checkUsersOAuthAppsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "OAuthApps",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersOAuthAuthDataIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "OAuthAuthData",
|
|
childIdAttr: "Code",
|
|
})
|
|
}
|
|
|
|
func checkUsersOutgoingWebhooksIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "CreatorId",
|
|
childName: "OutgoingWebhooks",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersPostsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Posts",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersPreferencesIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Preferences",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersReactionsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Reactions",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersSessionsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Sessions",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkUsersStatusIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "Status",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersTeamMembersIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "TeamMembers",
|
|
childIdAttr: "",
|
|
})
|
|
}
|
|
|
|
func checkUsersUserAccessTokensIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Users",
|
|
parentIdAttr: "UserId",
|
|
childName: "UserAccessTokens",
|
|
childIdAttr: "Id",
|
|
})
|
|
}
|
|
|
|
func checkChannelsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkChannelsCommandWebhooksIntegrity(ss)
|
|
results <- checkChannelsChannelMemberHistoryIntegrity(ss)
|
|
results <- checkChannelsChannelMembersIntegrity(ss)
|
|
results <- checkChannelsIncomingWebhooksIntegrity(ss)
|
|
results <- checkChannelsOutgoingWebhooksIntegrity(ss)
|
|
results <- checkChannelsPostsIntegrity(ss)
|
|
results <- checkChannelsFileInfoIntegrity(ss)
|
|
}
|
|
|
|
func checkCommandsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkCommandsCommandWebhooksIntegrity(ss)
|
|
}
|
|
|
|
func checkPostsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkPostsFileInfoIntegrity(ss)
|
|
results <- checkPostsPostsRootIdIntegrity(ss)
|
|
results <- checkPostsReactionsIntegrity(ss)
|
|
results <- checkThreadsTeamsIntegrity(ss)
|
|
}
|
|
|
|
func checkSchemesIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkSchemesChannelsIntegrity(ss)
|
|
results <- checkSchemesTeamsIntegrity(ss)
|
|
}
|
|
|
|
func checkSessionsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkSessionsAuditsIntegrity(ss)
|
|
}
|
|
|
|
func checkTeamsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkTeamsChannelsIntegrity(ss)
|
|
results <- checkTeamsCommandsIntegrity(ss)
|
|
results <- checkTeamsIncomingWebhooksIntegrity(ss)
|
|
results <- checkTeamsOutgoingWebhooksIntegrity(ss)
|
|
results <- checkTeamsTeamMembersIntegrity(ss)
|
|
}
|
|
|
|
func checkUsersIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
results <- checkUsersAuditsIntegrity(ss)
|
|
results <- checkUsersCommandWebhooksIntegrity(ss)
|
|
results <- checkUsersChannelMemberHistoryIntegrity(ss)
|
|
results <- checkUsersChannelMembersIntegrity(ss)
|
|
results <- checkUsersChannelsIntegrity(ss)
|
|
results <- checkUsersCommandsIntegrity(ss)
|
|
results <- checkUsersCompliancesIntegrity(ss)
|
|
results <- checkUsersEmojiIntegrity(ss)
|
|
results <- checkUsersFileInfoIntegrity(ss)
|
|
results <- checkUsersIncomingWebhooksIntegrity(ss)
|
|
results <- checkUsersOAuthAccessDataIntegrity(ss)
|
|
results <- checkUsersOAuthAppsIntegrity(ss)
|
|
results <- checkUsersOAuthAuthDataIntegrity(ss)
|
|
results <- checkUsersOutgoingWebhooksIntegrity(ss)
|
|
results <- checkUsersPostsIntegrity(ss)
|
|
results <- checkUsersPreferencesIntegrity(ss)
|
|
results <- checkUsersReactionsIntegrity(ss)
|
|
results <- checkUsersSessionsIntegrity(ss)
|
|
results <- checkUsersStatusIntegrity(ss)
|
|
results <- checkUsersTeamMembersIntegrity(ss)
|
|
results <- checkUsersUserAccessTokensIntegrity(ss)
|
|
}
|
|
|
|
func checkThreadsTeamsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
|
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
|
parentName: "Teams",
|
|
parentIdAttr: "ThreadTeamId",
|
|
childName: "Threads",
|
|
childIdAttr: "PostId",
|
|
canParentIdBeEmpty: false,
|
|
})
|
|
}
|
|
|
|
func CheckRelationalIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
|
|
mlog.Info("Starting relational integrity checks...")
|
|
checkChannelsIntegrity(ss, results)
|
|
checkCommandsIntegrity(ss, results)
|
|
checkPostsIntegrity(ss, results)
|
|
checkSchemesIntegrity(ss, results)
|
|
checkSessionsIntegrity(ss, results)
|
|
checkTeamsIntegrity(ss, results)
|
|
checkUsersIntegrity(ss, results)
|
|
mlog.Info("Done with relational integrity checks")
|
|
close(results)
|
|
}
|