This commit is contained in:
2025-10-10 19:23:53 +05:00
parent b5dc953d6b
commit 7bb50a4ee5
13 changed files with 961 additions and 79 deletions
+114 -33
View File
@@ -1,9 +1,11 @@
import { Elysia, t } from "elysia";
import { authMiddleware } from "../middlewares/auth";
import { optionalAuthMiddleware } from "../middlewares/optionalAuth";
import { eq } from "drizzle-orm";
import db from "../db";
import { apps } from "../db/schema/apps";
import { serverSessionService } from "../services/serverSession";
import { serverService } from "../services/server";
export const sessionController = new Elysia({ prefix: "/sessions" })
// PATCH /sessions/:id/status - обновить статус сессии (публичный endpoint для сессионного сервера)
@@ -78,45 +80,17 @@ export const sessionController = new Elysia({ prefix: "/sessions" })
}),
}
)
// Все роуты требуют авторизации
.use(authMiddleware)
// GET /sessions - получить список сессий пользователя
.get("/", async ({ currentUser, query }) => {
const { status, mode } = query as {
status?: "starting" | "started" | "ending" | "ended";
mode?: "stream" | "local";
};
const sessions = await serverSessionService.findByUserId(currentUser.id, {
status,
mode,
});
return { sessions };
})
// GET /sessions/:id - получить информацию о конкретной сессии
.get("/:id", async ({ params, currentUser, status }) => {
const { id } = params;
const session = await serverSessionService.findByIdForUser(
id,
currentUser.id
);
if (!session) {
return status(404, "Session not found");
}
return { session };
})
// POST /sessions - создать новую сессию
// Endpoints с optional auth (доступны для неавторизованных пользователей)
.use(optionalAuthMiddleware)
// POST /sessions - создать новую сессию (с optional auth для demo серверов)
.post(
"/",
async ({ body, currentUser, status }) => {
const { appId, mode, serverId } = body as {
const { appId, mode, serverId, tier } = body as {
appId: string;
mode: "stream" | "local";
serverId?: string;
tier?: "demo" | "prod";
};
// Проверить, что приложение существует
@@ -128,6 +102,53 @@ export const sessionController = new Elysia({ prefix: "/sessions" })
return status(404, "App not found");
}
// Если пользователь не авторизован
if (!currentUser) {
// Проверяем, что режим - stream (только stream поддерживает demo)
if (mode !== "stream") {
return status(401, "Authorization required for local sessions");
}
// Неавторизованные пользователи могут использовать только demo-серверы
if (tier && tier !== "demo") {
return status(
403,
"Unauthorized users can only use demo tier servers"
);
}
// Проверяем, что есть доступные demo-серверы
const demoServers = await serverService.findAvailableStreamServers(
"demo"
);
if (demoServers.length === 0) {
return status(
503,
"No available demo servers. Please login to use production servers."
);
}
// Создаем сессию без userId (для неавторизованных пользователей)
try {
const newSession = await serverSessionService.create({
appId,
// userId не передаем - будет undefined для неавторизованных пользователей
mode,
serverId,
tier: "demo", // Всегда demo для неавторизованных
});
return { session: newSession };
} catch (error) {
if (error instanceof Error) {
return status(503, error.message);
}
return status(500, "Failed to create session");
}
}
// Если пользователь авторизован - используем стандартную логику
// Проверить, что пользователь не имеет активных сессий этого приложения
const hasActive = await serverSessionService.hasActiveSession(
currentUser.id,
@@ -138,6 +159,16 @@ export const sessionController = new Elysia({ prefix: "/sessions" })
return status(409, "User already has an active session for this app");
}
// Для режима stream - проверяем наличие серверов нужного tier
if (mode === "stream" && tier) {
const availableServers = await serverService.findAvailableStreamServers(
tier
);
if (availableServers.length === 0) {
return status(503, `No available ${tier} servers`);
}
}
// Создать сессию
try {
const newSession = await serverSessionService.create({
@@ -145,6 +176,7 @@ export const sessionController = new Elysia({ prefix: "/sessions" })
userId: currentUser.id,
mode,
serverId,
tier,
});
return { session: newSession };
@@ -160,9 +192,58 @@ export const sessionController = new Elysia({ prefix: "/sessions" })
appId: t.String({ format: "uuid" }),
mode: t.Union([t.Literal("stream"), t.Literal("local")]),
serverId: t.Optional(t.String({ format: "uuid" })),
tier: t.Optional(t.Union([t.Literal("demo"), t.Literal("prod")])),
}),
}
)
// GET /sessions/:id - получить информацию о конкретной сессии (optional auth)
.get("/:id", async ({ params, currentUser, status }) => {
const { id } = params;
// Для авторизованных пользователей - проверяем ownership
if (currentUser) {
const session = await serverSessionService.findByIdForUser(
id,
currentUser.id
);
if (!session) {
return status(404, "Session not found");
}
return { session };
}
// Для неавторизованных - просто находим сессию по ID
const session = await serverSessionService.findById(id);
if (!session) {
return status(404, "Session not found");
}
// Проверяем, что это сессия без userId (неавторизованная)
if (session.userId) {
return status(403, "This session belongs to an authenticated user");
}
return { session };
})
// Все остальные роуты требуют авторизации
.use(authMiddleware)
// GET /sessions - получить список сессий пользователя
.get("/", async ({ currentUser, query }) => {
const { status, mode } = query as {
status?: "starting" | "started" | "ending" | "ended";
mode?: "stream" | "local";
};
const sessions = await serverSessionService.findByUserId(currentUser.id, {
status,
mode,
});
return { sessions };
})
// PATCH /sessions/:id - обновить статус сессии
.patch(
"/:id",