feat(instance): админ-панель с лимитами медиа и действиями над пользователями
- лимиты медиа живут в настройках инстанса (миграция 00016): аватары, оформление сервера, эмодзи, звуки и галерея; все загрузки берут предел оттуда, значения по умолчанию — из AGENT.md 7.7 - действия администратора: временный пароль (показывается один раз, сессии отзываются), выход со всех устройств, мягкое удаление пользователя (сообщения и аудит остаются), переименование и удаление любого сервера - админ-панель разбита на вкладки: обзор и здоровье, лимиты, пользователи, серверы, аудит; смена администраторов подтверждается личностью (step-up) - тесты: Go (действия администратора, лимит эмодзи из настроек) и Vitest (вкладки, сброс пароля, выход, удаление, переименование сервера)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -18,9 +19,24 @@ type InstanceSettings struct {
|
||||
MaxMembersPerGuild int
|
||||
MaxMessageLength int
|
||||
AuditRetentionDays int
|
||||
raw map[string]string
|
||||
// Лимиты медиа в байтах (AGENT.md 7.7): 0 — значение по умолчанию.
|
||||
MaxAvatarSize int64
|
||||
MaxGuildImageSize int64
|
||||
MaxEmojiSize int64
|
||||
MaxSoundSize int64
|
||||
MaxCosmeticSize int64
|
||||
raw map[string]string
|
||||
}
|
||||
|
||||
// MediaLimits по умолчанию: совпадают со спецификацией 7.7.
|
||||
const (
|
||||
DefaultAvatarSize int64 = 5 << 20
|
||||
DefaultGuildImageSize int64 = 5 << 20
|
||||
DefaultEmojiSize int64 = 512 << 10
|
||||
DefaultSoundSize int64 = 512 << 10
|
||||
DefaultCosmeticSize int64 = 5 << 20
|
||||
)
|
||||
|
||||
const instanceSettingsQuery = `SELECT key, value FROM instance_settings`
|
||||
|
||||
func (s *Store) InstanceSettings(ctx context.Context) (*InstanceSettings, error) {
|
||||
@@ -53,6 +69,11 @@ func settingsFromMap(values map[string]string) *InstanceSettings {
|
||||
MaxMembersPerGuild: parseSettingInt(values["max_members_per_guild"], 250),
|
||||
MaxMessageLength: parseSettingInt(values["max_message_length"], 4000),
|
||||
AuditRetentionDays: parseSettingInt(values["audit_retention_days"], 90),
|
||||
MaxAvatarSize: parseSettingInt64(values["max_avatar_size"], DefaultAvatarSize),
|
||||
MaxGuildImageSize: parseSettingInt64(values["max_guild_image_size"], DefaultGuildImageSize),
|
||||
MaxEmojiSize: parseSettingInt64(values["max_emoji_size"], DefaultEmojiSize),
|
||||
MaxSoundSize: parseSettingInt64(values["max_sound_size"], DefaultSoundSize),
|
||||
MaxCosmeticSize: parseSettingInt64(values["max_cosmetic_size"], DefaultCosmeticSize),
|
||||
raw: values,
|
||||
}
|
||||
if mainGuild := values["main_guild_id"]; mainGuild != "" {
|
||||
@@ -161,6 +182,18 @@ func optionalID(value sql.NullInt64) *uint64 {
|
||||
return &converted
|
||||
}
|
||||
|
||||
// parseSettingInt64 читает настройку-размер в байтах.
|
||||
func parseSettingInt64(value string, fallback int64) int64 {
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
parsed, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil || parsed <= 0 {
|
||||
return fallback
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
func parseSettingInt(value string, fallback int) int {
|
||||
parsed, ok := parseUint(value)
|
||||
if !ok || parsed > math.MaxInt32 {
|
||||
|
||||
Reference in New Issue
Block a user