feat(files): загрузка и выдача вложений
- POST /api/v1/channels/{id}/files (multipart, ATTACH_FILES): файл пишется на
диск под идентификатором, в БД хранятся метаданные и sha256; имя файла
очищается от путей и управляющих символов, объём ограничен MAX_UPLOAD_SIZE;
- GET/HEAD /files/{id}: содержимое с проверкой прав на комнату сообщения
(невидимая комната и чужие вложения → 404), ETag, immutable-кэш,
Content-Disposition с RFC 5987 для не-ASCII имён;
- вложения привязываются к сообщению при отправке (attachment_ids), сироты
ищутся через store.ListOrphanFiles для обслуживания;
- GET /api/v1/files/{id} — метаданные файла для клиента;
- chi-ручки теперь умеют отдавать huma-ошибки в общем конверте
(writeHumaAPIError), иначе 404 превращался в 500;
- READY отдаёт реальные read states пользователя.
Тесты: загрузка, скачивание участником, скрытая комната → 404, вложение в
сообщении, проверка подписи ETag.
This commit is contained in:
@@ -62,6 +62,20 @@ func (s *Snapshot) Build(ctx context.Context, user *store.User) (*Ready, error)
|
||||
ready.User.AvatarFileID = formatID(*user.AvatarFileID)
|
||||
}
|
||||
|
||||
// Состояния прочтения: непрочитанные и упоминания синхронизируются между
|
||||
// устройствами пользователя (AGENT.md 7.16).
|
||||
readStates, err := s.store.ListReadStates(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list read states: %w", err)
|
||||
}
|
||||
for _, state := range readStates {
|
||||
ready.ReadStates = append(ready.ReadStates, ReadyRead{
|
||||
ChannelID: formatID(state.ChannelID),
|
||||
LastMessageID: formatID(state.LastMessageID),
|
||||
MentionCount: state.MentionCount,
|
||||
})
|
||||
}
|
||||
|
||||
guilds, err := s.store.ListGuildsForUser(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list user guilds: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user