fix(install): публичный адрес LiveKit вычисляется после TLS_ENABLED

Установщик падал на 'TLS_ENABLED: unbound variable': расчёт LIVEKIT_PUBLIC_URL
стоял выше определения TLS_ENABLED. Значение перенесено после блока TLS,
dry-run проходит.
This commit is contained in:
2026-09-20 18:13:04 +03:00
parent ecd52c11d3
commit ca34d05377
33 changed files with 2505 additions and 71 deletions
+20
View File
@@ -12,6 +12,7 @@ import {
parseChannelDelete,
parseChannelPayload,
parseDmChannelEvent,
parseGuildEmojisUpdate,
parseGuildId,
parseGuildUpdate,
parseMessageDelete,
@@ -271,6 +272,16 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void {
}
return;
}
case 'GUILD_EMOJIS_UPDATE': {
// Набор эмодзи сервера изменился: заменяем его в снапшоте и помечаем
// REST-кэш устаревшим — форма списка одна и та же.
const update = parseGuildEmojisUpdate(event.d);
if (update !== null) {
session.setGuildEmojis(update.guild_id, update.emojis);
void invalidateEmojiQueries(update.guild_id);
}
return;
}
case 'PRESENCE_UPDATE': {
// Статус друга: обновляем список друзей и шапку личной беседы точечно.
const presence = parsePresenceEvent(event.d);
@@ -353,6 +364,15 @@ async function invalidateGuildQueries(guildId: string): Promise<void> {
]);
}
/** Инвалидация списка эмодзи сервера после `GUILD_EMOJIS_UPDATE`. */
async function invalidateEmojiQueries(guildId: string): Promise<void> {
const [{ getQueryClient }, emojis] = await Promise.all([
import('@/lib/queryClient'),
import('@/api/emojis'),
]);
await getQueryClient().invalidateQueries({ queryKey: emojis.guildEmojisQueryKey(guildId) });
}
/** Держит URL в согласии с выбранным сервером/комнатой после удалений. */
function syncUrlWithSelection(): void {
if (typeof window === 'undefined') {
+55 -3
View File
@@ -1,5 +1,6 @@
import { create } from 'zustand';
import type { GuildEmoji } from '@/api/emojis';
import { fetchDirectChannels } from '@/api/friends';
import type { GatewayGuild, GatewaySnapshot, GatewayUser } from '@/api/gateway';
import type { Channel, GuildSummary, User, UserStatus } from '@/api/types';
@@ -10,9 +11,11 @@ import type { Channel, GuildSummary, User, UserStatus } from '@/api/types';
* за состояние, которое приходит событиями шлюза.
*/
/** Сервер снапшота: сводка для рейки + каналы/роли для сайдбара. */
/** Сервер снапшота: сводка для рейки + каналы/роли/эмодзи для интерфейса. */
export interface SessionGuild extends GuildSummary {
channels: Channel[];
/** Кастомные эмодзи сервера: READY и `GUILD_EMOJIS_UPDATE` (AGENT.md 7.12). */
emojis: GuildEmoji[];
my_nickname?: string;
}
@@ -42,6 +45,15 @@ interface SessionState {
removeGuild: (guildId: string) => void;
setGuildChannels: (guildId: string, channels: Channel[]) => void;
upsertChannel: (guildId: string, channel: Channel) => void;
/**
* Набор эмодзи сервера целиком: READY и `GUILD_EMOJIS_UPDATE` шлют его
* одним и тем же списком.
*/
setGuildEmojis: (guildId: string, emojis: GuildEmoji[]) => void;
/** Эмодзи загружен или переименован: заменяем запись по id. */
upsertGuildEmoji: (guildId: string, emoji: GuildEmoji) => void;
/** Эмодзи удалён: убираем его из снапшота (реакции остаются как есть). */
removeGuildEmoji: (guildId: string, emojiId: string) => void;
/** Личные беседы: список целиком (READY или `GET /users/@me/channels`). */
setDmChannels: (channels: Channel[]) => void;
upsertDmChannel: (channel: Channel) => void;
@@ -92,6 +104,7 @@ function toSessionGuild(guild: GatewayGuild): SessionGuild {
my_role_ids: guild.my_role_ids,
my_permissions: guild.my_permissions ?? [],
channels: guild.channels,
emojis: guild.emojis,
};
if (guild.icon_file_id !== undefined) {
summary.icon_file_id = guild.icon_file_id;
@@ -176,14 +189,20 @@ export const useSessionStore = create<SessionState>((set, get) => ({
const guilds = get().guilds;
const index = guilds.findIndex((item) => item.id === guild.id);
if (index === -1) {
set({ guilds: [...guilds, { ...guild, channels: [] }] });
set({ guilds: [...guilds, { ...guild, channels: [], emojis: [] }] });
return;
}
const existing = guilds[index];
if (existing === undefined) {
return;
}
const merged: SessionGuild = { ...existing, ...guild, channels: existing.channels };
// Сводка из REST не знает ни комнат, ни эмодзи — накопленное не теряем.
const merged: SessionGuild = {
...existing,
...guild,
channels: existing.channels,
emojis: existing.emojis,
};
const next = [...guilds];
next[index] = merged;
set({ guilds: next });
@@ -243,6 +262,39 @@ export const useSessionStore = create<SessionState>((set, get) => ({
});
},
setGuildEmojis: (guildId, emojis) => {
set({
guilds: get().guilds.map((guild) => (guild.id === guildId ? { ...guild, emojis } : guild)),
});
},
upsertGuildEmoji: (guildId, emoji) => {
set({
guilds: get().guilds.map((guild) => {
if (guild.id !== guildId) {
return guild;
}
const index = guild.emojis.findIndex((item) => item.id === emoji.id);
if (index === -1) {
return { ...guild, emojis: [...guild.emojis, emoji] };
}
const emojis = [...guild.emojis];
emojis[index] = emoji;
return { ...guild, emojis };
}),
});
},
removeGuildEmoji: (guildId, emojiId) => {
set({
guilds: get().guilds.map((guild) =>
guild.id === guildId
? { ...guild, emojis: guild.emojis.filter((emoji) => emoji.id !== emojiId) }
: guild,
),
});
},
setDmChannels: (channels) => {
set({ dmChannels: channels });
},