feat(guilds): оформление сервера — иконка, баннер, splash и акцентный цвет
Фаза 4 (AGENT.md 7.4): сервер можно оформить, поля в схеме были с Фазы 1, но
ручек и отдачи в API не было.
- `POST/DELETE /guilds/{id}/appearance/{icon|banner|splash}` (multipart,
MANAGE_GUILD, только изображения до 8 МБ): файл сохраняется назначением
`guild_icon|guild_banner|guild_splash`, предыдущий удаляется, пишется аудит
и уходит `GUILD_UPDATE`; ответ — обновлённый сервер;
- `PATCH /guilds/{id}` принимает `accent_color` (0 — цвет темы);
- оформление отдаётся в READY, в списке серверов, в детальной карточке и в
предпросмотре приглашения — клиенту не нужен отдельный запрос;
- анимированные GIF/APNG/WebP сохраняются как есть: изображения не
перекодируются, поэтому анимация не теряется;
- тесты: загрузка и очистка всех трёх видов, замена файла, запрет без
MANAGE_GUILD и без сессии, отказ для не-изображения, акцентный цвет.
This commit is contained in:
@@ -63,6 +63,9 @@ type guildDetailPayload struct {
|
||||
Description string `json:"description"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
IconFileID string `json:"icon_file_id,omitempty"`
|
||||
BannerFileID string `json:"banner_file_id,omitempty"`
|
||||
SplashFileID string `json:"splash_file_id,omitempty"`
|
||||
AccentColor int64 `json:"accent_color,omitempty"`
|
||||
IsMain bool `json:"is_main"`
|
||||
MemberCount int `json:"member_count"`
|
||||
Roles []rolePayload `json:"roles"`
|
||||
@@ -224,6 +227,8 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
|
||||
// Публичность сервера: попадает в каталог и открывает вход без
|
||||
// приглашения (AGENT.md 7.20).
|
||||
Public *bool `json:"public,omitempty"`
|
||||
// Акцентный цвет сервера (0 — цвет темы по умолчанию).
|
||||
AccentColor *int64 `json:"accent_color,omitempty" minimum:"0" maximum:"16777215"`
|
||||
}
|
||||
},
|
||||
) (*guildOutput, error) {
|
||||
@@ -235,7 +240,10 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := store.UpdateGuildParams{Description: input.Body.Description}
|
||||
params := store.UpdateGuildParams{
|
||||
Description: input.Body.Description,
|
||||
AccentColor: input.Body.AccentColor,
|
||||
}
|
||||
if input.Body.Public != nil {
|
||||
// Главный сервер публичным не делаем: он и так точка входа.
|
||||
if *input.Body.Public {
|
||||
@@ -661,6 +669,13 @@ func (s *Server) guildOutput(ctx context.Context, user *store.User, guild store.
|
||||
if guild.IconFileID != nil {
|
||||
payload.IconFileID = formatSnowflake(*guild.IconFileID)
|
||||
}
|
||||
if guild.BannerFileID != nil {
|
||||
payload.BannerFileID = formatSnowflake(*guild.BannerFileID)
|
||||
}
|
||||
if guild.SplashFileID != nil {
|
||||
payload.SplashFileID = formatSnowflake(*guild.SplashFileID)
|
||||
}
|
||||
payload.AccentColor = guild.AccentColor
|
||||
roleIDs, err := s.store.MemberRoleIDs(ctx, guild.ID, user.ID)
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
|
||||
Reference in New Issue
Block a user