feat(api): ручки Фаз(ы) 1 — профиль, серверы, роли, админ инстанса
REST-слой Фазы 1 на huma (OpenAPI 3.1 генерируется из кода):
- профиль: GET/PATCH /users/@me, смена пароля со step-up, публичный профиль,
завершение онбординга (новая миграция 00003 с onboarding_completed_at);
- серверы: создание/изменение/удаление, join/leave, список серверов
пользователя, журнал действий;
- комнаты: список с учётом прав, создание/изменение/удаление;
- участники: список с профилями и ролями, никнейм, тайм-аут, исключение;
- роли: CRUD, выдача/снятие с проверкой иерархии и запретом выдачи прав выше
собственных;
- админ инстанса: публичная информация, настройки, серверы, пользователи,
аудит, выдача прав администратора со step-up; обход лимитов фиксируется в
аудите отдельной записью limits.bypass;
- движок прав: участие в сервере стало обязательным условием (IsMember),
не участник не получает прав роли @user; калькулятор прав общий для API и
Gateway, инвалидация кэша после изменений;
- Gateway: браузерный клиент аутентифицируется cookie на рукопожатии, IDENTIFY
без токена использует её; события GUILD/CHANNEL/MEMBER/ROLE рассылаются из
ручек, USER_UPDATE — адресно;
- ошибки huma отдаются в едином конверте {"error":{"code","message"}}.
Тесты: 8 сценариев API (профиль, жизненный цикл сервера и права, лимиты и
обход админом, иерархия ролей, тайм-аут, скрытие комнаты оверрайдом,
членство в движке прав, cookie-идентификация Gateway).
This commit is contained in:
@@ -171,12 +171,20 @@ func (r *Runner) findOrCreateAdmin(ctx context.Context, opts Options, result *Re
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ensureMainGuildSetup гарантирует роли по умолчанию, участие владельца и
|
||||
// выдачу роли «Администратор» (AGENT.md 6.3).
|
||||
func (r *Runner) ensureMainGuildSetup(ctx context.Context, guild *store.Guild, owner *store.User, result *Result) error {
|
||||
roles, err := r.store.ListGuildRoles(ctx, guild.ID)
|
||||
// SeedResult — роли, созданные при настройке сервера.
|
||||
type SeedResult struct {
|
||||
AdminRoleID uint64
|
||||
MemberRoleID uint64
|
||||
}
|
||||
|
||||
// SeedGuildDefaults создаёт роли по умолчанию, добавляет владельца в сервер и
|
||||
// выдаёт ему роль «Администратор» (AGENT.md 6.3). Функция идемпотентна: её
|
||||
// используют и установщик, и ручка создания сервера.
|
||||
func SeedGuildDefaults(ctx context.Context, st *store.Store, guild *store.Guild, owner *store.User) (*SeedResult, error) {
|
||||
result := &SeedResult{}
|
||||
roles, err := st.ListGuildRoles(ctx, guild.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
var adminRole, defaultRole *store.Role
|
||||
for i := range roles {
|
||||
@@ -188,35 +196,47 @@ func (r *Runner) ensureMainGuildSetup(ctx context.Context, guild *store.Guild, o
|
||||
}
|
||||
}
|
||||
if adminRole == nil {
|
||||
created, err := r.store.CreateRole(ctx, store.CreateRoleParams{
|
||||
created, err := st.CreateRole(ctx, store.CreateRoleParams{
|
||||
GuildID: guild.ID, Name: "Администратор",
|
||||
Permissions: uint64(permissions.AllPermissions), Position: 100, Mentionable: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create administrator role: %w", err)
|
||||
return nil, fmt.Errorf("create administrator role: %w", err)
|
||||
}
|
||||
adminRole = created
|
||||
}
|
||||
result.AdminRoleID = adminRole.ID
|
||||
if defaultRole == nil {
|
||||
created, err := r.store.CreateRole(ctx, store.CreateRoleParams{
|
||||
created, err := st.CreateRole(ctx, store.CreateRoleParams{
|
||||
GuildID: guild.ID, Name: "Пользователь",
|
||||
Permissions: uint64(permissions.DefaultUserPermissions), Position: 0,
|
||||
IsDefault: true, Mentionable: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create default role: %w", err)
|
||||
return nil, fmt.Errorf("create default role: %w", err)
|
||||
}
|
||||
defaultRole = created
|
||||
}
|
||||
result.MemberRoleID = defaultRole.ID
|
||||
|
||||
if _, err := r.store.AddGuildMember(ctx, guild.ID, owner.ID, ""); err != nil {
|
||||
return fmt.Errorf("add owner to main guild: %w", err)
|
||||
if _, err := st.AddGuildMember(ctx, guild.ID, owner.ID, ""); err != nil {
|
||||
return nil, fmt.Errorf("add owner to guild: %w", err)
|
||||
}
|
||||
if err := r.store.AssignRole(ctx, guild.ID, owner.ID, adminRole.ID); err != nil {
|
||||
return fmt.Errorf("assign administrator role: %w", err)
|
||||
if err := st.AssignRole(ctx, guild.ID, owner.ID, adminRole.ID); err != nil {
|
||||
return nil, fmt.Errorf("assign administrator role: %w", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ensureMainGuildSetup гарантирует роли по умолчанию, участие владельца и
|
||||
// выдачу роли «Администратор» (AGENT.md 6.3).
|
||||
func (r *Runner) ensureMainGuildSetup(ctx context.Context, guild *store.Guild, owner *store.User, result *Result) error {
|
||||
seeded, err := SeedGuildDefaults(ctx, r.store, guild, owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result.AdminRoleID = seeded.AdminRoleID
|
||||
result.MemberRoleID = seeded.MemberRoleID
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user