feat: add fluxer upstream source and self-hosting documentation
- Clone of github.com/fluxerapp/fluxer (official upstream) - SELF_HOSTING.md: full VM rebuild procedure, architecture overview, service reference, step-by-step setup, troubleshooting, seattle reference - dev/.env.example: all env vars with secrets redacted and generation instructions - dev/livekit.yaml: LiveKit config template with placeholder keys - fluxer-seattle/: existing seattle deployment setup scripts
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
type ElasticsearchFieldType = 'text' | 'keyword' | 'boolean' | 'long' | 'integer' | 'date' | 'float';
|
||||
|
||||
export type FluxerSearchIndexName = 'messages' | 'guilds' | 'users' | 'reports' | 'audit_logs' | 'guild_members';
|
||||
|
||||
export interface ElasticsearchFieldMapping {
|
||||
type: ElasticsearchFieldType;
|
||||
index?: boolean;
|
||||
fields?: Record<string, ElasticsearchFieldMapping>;
|
||||
}
|
||||
|
||||
export interface ElasticsearchIndexSettings {
|
||||
number_of_shards?: number;
|
||||
number_of_replicas?: number;
|
||||
}
|
||||
|
||||
export interface ElasticsearchIndexDefinition {
|
||||
indexName: FluxerSearchIndexName;
|
||||
mappings: {
|
||||
properties: Record<string, ElasticsearchFieldMapping>;
|
||||
};
|
||||
settings?: ElasticsearchIndexSettings;
|
||||
}
|
||||
|
||||
function textWithKeyword(): ElasticsearchFieldMapping {
|
||||
return {type: 'text', fields: {keyword: {type: 'keyword'}}};
|
||||
}
|
||||
|
||||
function keyword(): ElasticsearchFieldMapping {
|
||||
return {type: 'keyword'};
|
||||
}
|
||||
|
||||
function bool(): ElasticsearchFieldMapping {
|
||||
return {type: 'boolean'};
|
||||
}
|
||||
|
||||
function long(): ElasticsearchFieldMapping {
|
||||
return {type: 'long'};
|
||||
}
|
||||
|
||||
function integer(): ElasticsearchFieldMapping {
|
||||
return {type: 'integer'};
|
||||
}
|
||||
|
||||
export const ELASTICSEARCH_INDEX_DEFINITIONS: Record<FluxerSearchIndexName, ElasticsearchIndexDefinition> = {
|
||||
messages: {
|
||||
indexName: 'messages',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: keyword(),
|
||||
channelId: keyword(),
|
||||
guildId: keyword(),
|
||||
authorId: keyword(),
|
||||
authorType: keyword(),
|
||||
content: textWithKeyword(),
|
||||
createdAt: long(),
|
||||
editedAt: long(),
|
||||
isPinned: bool(),
|
||||
mentionedUserIds: keyword(),
|
||||
mentionEveryone: bool(),
|
||||
hasLink: bool(),
|
||||
hasEmbed: bool(),
|
||||
hasPoll: bool(),
|
||||
hasFile: bool(),
|
||||
hasVideo: bool(),
|
||||
hasImage: bool(),
|
||||
hasSound: bool(),
|
||||
hasSticker: bool(),
|
||||
hasForward: bool(),
|
||||
embedTypes: keyword(),
|
||||
embedProviders: keyword(),
|
||||
linkHostnames: keyword(),
|
||||
attachmentFilenames: keyword(),
|
||||
attachmentExtensions: keyword(),
|
||||
},
|
||||
},
|
||||
},
|
||||
guilds: {
|
||||
indexName: 'guilds',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: keyword(),
|
||||
ownerId: keyword(),
|
||||
name: textWithKeyword(),
|
||||
vanityUrlCode: textWithKeyword(),
|
||||
discoveryDescription: textWithKeyword(),
|
||||
iconHash: keyword(),
|
||||
bannerHash: keyword(),
|
||||
splashHash: keyword(),
|
||||
features: keyword(),
|
||||
verificationLevel: integer(),
|
||||
mfaLevel: integer(),
|
||||
nsfwLevel: integer(),
|
||||
createdAt: long(),
|
||||
discoveryCategory: integer(),
|
||||
isDiscoverable: bool(),
|
||||
},
|
||||
},
|
||||
},
|
||||
users: {
|
||||
indexName: 'users',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: textWithKeyword(),
|
||||
username: textWithKeyword(),
|
||||
email: textWithKeyword(),
|
||||
phone: textWithKeyword(),
|
||||
discriminator: integer(),
|
||||
isBot: bool(),
|
||||
isSystem: bool(),
|
||||
flags: keyword(),
|
||||
premiumType: integer(),
|
||||
emailVerified: bool(),
|
||||
emailBounced: bool(),
|
||||
suspiciousActivityFlags: integer(),
|
||||
acls: keyword(),
|
||||
createdAt: long(),
|
||||
lastActiveAt: long(),
|
||||
tempBannedUntil: long(),
|
||||
pendingDeletionAt: long(),
|
||||
stripeSubscriptionId: keyword(),
|
||||
stripeCustomerId: keyword(),
|
||||
},
|
||||
},
|
||||
},
|
||||
reports: {
|
||||
indexName: 'reports',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: keyword(),
|
||||
reporterId: keyword(),
|
||||
reportedAt: long(),
|
||||
status: integer(),
|
||||
reportType: integer(),
|
||||
category: textWithKeyword(),
|
||||
additionalInfo: textWithKeyword(),
|
||||
reportedUserId: keyword(),
|
||||
reportedGuildId: keyword(),
|
||||
reportedGuildName: textWithKeyword(),
|
||||
reportedMessageId: keyword(),
|
||||
reportedChannelId: keyword(),
|
||||
reportedChannelName: textWithKeyword(),
|
||||
guildContextId: keyword(),
|
||||
resolvedAt: long(),
|
||||
resolvedByAdminId: keyword(),
|
||||
publicComment: keyword(),
|
||||
createdAt: long(),
|
||||
},
|
||||
},
|
||||
},
|
||||
audit_logs: {
|
||||
indexName: 'audit_logs',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: keyword(),
|
||||
logId: keyword(),
|
||||
adminUserId: keyword(),
|
||||
targetType: textWithKeyword(),
|
||||
targetId: textWithKeyword(),
|
||||
action: textWithKeyword(),
|
||||
auditLogReason: textWithKeyword(),
|
||||
createdAt: long(),
|
||||
},
|
||||
},
|
||||
},
|
||||
guild_members: {
|
||||
indexName: 'guild_members',
|
||||
mappings: {
|
||||
properties: {
|
||||
id: keyword(),
|
||||
guildId: keyword(),
|
||||
userId: textWithKeyword(),
|
||||
username: textWithKeyword(),
|
||||
discriminator: textWithKeyword(),
|
||||
globalName: textWithKeyword(),
|
||||
nickname: textWithKeyword(),
|
||||
roleIds: keyword(),
|
||||
joinedAt: long(),
|
||||
joinSourceType: integer(),
|
||||
sourceInviteCode: keyword(),
|
||||
inviterId: keyword(),
|
||||
userCreatedAt: long(),
|
||||
isBot: bool(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user