Refactor Protected and Public Routes for Consistent Loading UI; Enhance HomePage with User Company and Branch Details; Update LoginPage Layout; Introduce User Relations in Auth Services
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
loginService,
|
||||
registerService,
|
||||
sessionService,
|
||||
userService,
|
||||
} from "../services/auth";
|
||||
import type { LoginData, RegisterData } from "../services/auth/types";
|
||||
|
||||
@@ -43,7 +44,17 @@ export const authController = new Elysia({ prefix: "/auth" })
|
||||
.use(authMiddleware)
|
||||
// GET /me
|
||||
.get("/me", async ({ currentUser }) => {
|
||||
return { user: currentUser };
|
||||
// Получить полную информацию о пользователе с филиалом и компанией
|
||||
const userWithRelations = await userService.findByIdWithRelations(
|
||||
currentUser.id
|
||||
);
|
||||
|
||||
if (!userWithRelations) {
|
||||
return { user: currentUser };
|
||||
}
|
||||
|
||||
const userResponse = userService.sanitizeWithRelations(userWithRelations);
|
||||
return { user: userResponse };
|
||||
})
|
||||
// POST /logout
|
||||
.post("/logout", async ({ authSession }) => {
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import { authMiddleware } from "../middlewares/auth";
|
||||
import { branchService } from "../services/branch";
|
||||
import { companyService } from "../services/company";
|
||||
|
||||
export const branchController = new Elysia({ prefix: "/branches" })
|
||||
// Все роуты требуют авторизации
|
||||
.use(authMiddleware)
|
||||
// GET /branches - получить филиалы пользователя
|
||||
.get("/my", async ({ currentUser }) => {
|
||||
const branches = await branchService.findByUserId(currentUser.id);
|
||||
return { branches };
|
||||
})
|
||||
// GET /branches/:id - получить филиал по ID
|
||||
.get("/:id", async ({ params, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
return { branch };
|
||||
})
|
||||
// GET /branches/:id/users - получить пользователей филиала
|
||||
.get("/:id/users", async ({ params, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
// Проверить существование филиала
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
const users = await branchService.getUsersByBranchId(id);
|
||||
|
||||
return { users };
|
||||
})
|
||||
// POST /branches - создать филиал
|
||||
.post(
|
||||
"/",
|
||||
async ({ body, status }) => {
|
||||
const { companyId, name, address, city, country } = body as {
|
||||
companyId: string;
|
||||
name: string;
|
||||
address?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
};
|
||||
|
||||
// Проверить существование компании
|
||||
const company = await companyService.findById(companyId);
|
||||
|
||||
if (!company) {
|
||||
return status(404, "Company not found");
|
||||
}
|
||||
|
||||
const branch = await branchService.create({
|
||||
companyId,
|
||||
name,
|
||||
address,
|
||||
city,
|
||||
country,
|
||||
});
|
||||
|
||||
return { branch };
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
companyId: t.String({ format: "uuid" }),
|
||||
name: t.String({ minLength: 1, maxLength: 255 }),
|
||||
address: t.Optional(t.String({ maxLength: 500 })),
|
||||
city: t.Optional(t.String({ maxLength: 100 })),
|
||||
country: t.Optional(t.String({ maxLength: 100 })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// PATCH /branches/:id - обновить филиал
|
||||
.patch(
|
||||
"/:id",
|
||||
async ({ params, body, status }) => {
|
||||
const { id } = params;
|
||||
const { name, address, city, country } = body as {
|
||||
name?: string;
|
||||
address?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
};
|
||||
|
||||
// Проверить существование филиала
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
const updatedBranch = await branchService.update(id, {
|
||||
name,
|
||||
address,
|
||||
city,
|
||||
country,
|
||||
});
|
||||
|
||||
return { branch: updatedBranch };
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
name: t.Optional(t.String({ minLength: 1, maxLength: 255 })),
|
||||
address: t.Optional(t.String({ maxLength: 500 })),
|
||||
city: t.Optional(t.String({ maxLength: 100 })),
|
||||
country: t.Optional(t.String({ maxLength: 100 })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// DELETE /branches/:id - удалить филиал
|
||||
.delete("/:id", async ({ params, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
// Проверить существование филиала
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
await branchService.delete(id);
|
||||
|
||||
return { message: "Branch deleted successfully" };
|
||||
})
|
||||
// POST /branches/:id/users - привязать пользователя к филиалу
|
||||
.post(
|
||||
"/:id/users",
|
||||
async ({ params, body, status }) => {
|
||||
const { id } = params;
|
||||
const { userId } = body as { userId: string };
|
||||
|
||||
// Проверить существование филиала
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
try {
|
||||
await branchService.assignUserToBranch(userId, id);
|
||||
return { message: "User assigned to branch successfully" };
|
||||
} catch (error) {
|
||||
return status(409, "User is already assigned to this branch");
|
||||
}
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
userId: t.String({ format: "uuid" }),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// DELETE /branches/:id/users/:userId - отвязать пользователя от филиала
|
||||
.delete("/:id/users/:userId", async ({ params, status }) => {
|
||||
const { id, userId } = params;
|
||||
|
||||
// Проверить существование филиала
|
||||
const branch = await branchService.findById(id);
|
||||
|
||||
if (!branch) {
|
||||
return status(404, "Branch not found");
|
||||
}
|
||||
|
||||
await branchService.removeUserFromBranch(userId, id);
|
||||
|
||||
return { message: "User removed from branch successfully" };
|
||||
})
|
||||
// POST /branches/:id/select - установить филиал как текущий для пользователя
|
||||
.post("/:id/select", async ({ params, currentUser, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
try {
|
||||
const updatedUser = await branchService.setUserCurrentBranch(
|
||||
currentUser.id,
|
||||
id
|
||||
);
|
||||
|
||||
return {
|
||||
message: "Current branch updated successfully",
|
||||
currentBranchId: updatedUser.currentBranchId,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return status(400, error.message);
|
||||
}
|
||||
return status(500, "Failed to update current branch");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import { authMiddleware } from "../middlewares/auth";
|
||||
import { companyService } from "../services/company";
|
||||
|
||||
export const companyController = new Elysia({ prefix: "/companies" })
|
||||
// Все роуты требуют авторизации
|
||||
.use(authMiddleware)
|
||||
// GET /companies - получить все компании
|
||||
.get("/", async () => {
|
||||
const companies = await companyService.findAll();
|
||||
return { companies };
|
||||
})
|
||||
// GET /companies/:id - получить компанию по ID
|
||||
.get("/:id", async ({ params, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
const company = await companyService.findById(id);
|
||||
|
||||
if (!company) {
|
||||
return status(404, "Company not found");
|
||||
}
|
||||
|
||||
return { company };
|
||||
})
|
||||
// POST /companies - создать компанию
|
||||
.post(
|
||||
"/",
|
||||
async ({ body }) => {
|
||||
const { name, description } = body as {
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const company = await companyService.create({
|
||||
name,
|
||||
description,
|
||||
});
|
||||
|
||||
return { company };
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
name: t.String({ minLength: 1, maxLength: 255 }),
|
||||
description: t.Optional(t.String({ maxLength: 1000 })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// PATCH /companies/:id - обновить компанию
|
||||
.patch(
|
||||
"/:id",
|
||||
async ({ params, body, status }) => {
|
||||
const { id } = params;
|
||||
const { name, description } = body as {
|
||||
name?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
// Проверить существование компании
|
||||
const company = await companyService.findById(id);
|
||||
|
||||
if (!company) {
|
||||
return status(404, "Company not found");
|
||||
}
|
||||
|
||||
const updatedCompany = await companyService.update(id, {
|
||||
name,
|
||||
description,
|
||||
});
|
||||
|
||||
return { company: updatedCompany };
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
name: t.Optional(t.String({ minLength: 1, maxLength: 255 })),
|
||||
description: t.Optional(t.String({ maxLength: 1000 })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// DELETE /companies/:id - удалить компанию
|
||||
.delete("/:id", async ({ params, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
// Проверить существование компании
|
||||
const company = await companyService.findById(id);
|
||||
|
||||
if (!company) {
|
||||
return status(404, "Company not found");
|
||||
}
|
||||
|
||||
await companyService.delete(id);
|
||||
|
||||
return { message: "Company deleted successfully" };
|
||||
});
|
||||
@@ -0,0 +1,208 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import { authMiddleware } from "../middlewares/auth";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "../db";
|
||||
import { apps } from "../db/schema/apps";
|
||||
import { serverSessionService } from "../services/serverSession";
|
||||
|
||||
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 - создать новую сессию
|
||||
.post(
|
||||
"/",
|
||||
async ({ body, currentUser, status }) => {
|
||||
const { appId, mode, serverId } = body as {
|
||||
appId: string;
|
||||
mode: "stream" | "local";
|
||||
serverId?: string;
|
||||
};
|
||||
|
||||
// Проверить, что приложение существует
|
||||
const app = await db.query.apps.findFirst({
|
||||
where: eq(apps.id, appId),
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
return status(404, "App not found");
|
||||
}
|
||||
|
||||
// Проверить, что пользователь не имеет активных сессий этого приложения
|
||||
const hasActive = await serverSessionService.hasActiveSession(
|
||||
currentUser.id,
|
||||
appId
|
||||
);
|
||||
|
||||
if (hasActive) {
|
||||
return status(409, "User already has an active session for this app");
|
||||
}
|
||||
|
||||
// Создать сессию
|
||||
try {
|
||||
const newSession = await serverSessionService.create({
|
||||
appId,
|
||||
userId: currentUser.id,
|
||||
mode,
|
||||
serverId,
|
||||
});
|
||||
|
||||
return { session: newSession };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return status(503, error.message);
|
||||
}
|
||||
return status(500, "Failed to create session");
|
||||
}
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
appId: t.String({ format: "uuid" }),
|
||||
mode: t.Union([t.Literal("stream"), t.Literal("local")]),
|
||||
serverId: t.Optional(t.String({ format: "uuid" })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// PATCH /sessions/:id - обновить статус сессии
|
||||
.patch(
|
||||
"/:id",
|
||||
async ({ params, body, currentUser, status }) => {
|
||||
const { id } = params;
|
||||
const {
|
||||
status: sessionStatus,
|
||||
appPid,
|
||||
cirrusPid,
|
||||
endAt,
|
||||
} = body as {
|
||||
status?: "starting" | "started" | "ending" | "ended";
|
||||
appPid?: number;
|
||||
cirrusPid?: number;
|
||||
endAt?: string;
|
||||
};
|
||||
|
||||
// Проверить, что сессия существует и принадлежит пользователю
|
||||
const session = await serverSessionService.findByIdForUser(
|
||||
id,
|
||||
currentUser.id
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
return status(404, "Session not found");
|
||||
}
|
||||
|
||||
// Обновить сессию
|
||||
const updatedSession = await serverSessionService.update(id, {
|
||||
status: sessionStatus,
|
||||
appPid,
|
||||
cirrusPid,
|
||||
endAt: endAt ? new Date(endAt) : undefined,
|
||||
});
|
||||
|
||||
return { session: updatedSession };
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
status: t.Optional(
|
||||
t.Union([
|
||||
t.Literal("starting"),
|
||||
t.Literal("started"),
|
||||
t.Literal("ending"),
|
||||
t.Literal("ended"),
|
||||
])
|
||||
),
|
||||
appPid: t.Optional(t.Number()),
|
||||
cirrusPid: t.Optional(t.Number()),
|
||||
endAt: t.Optional(t.String({ format: "date-time" })),
|
||||
}),
|
||||
}
|
||||
)
|
||||
// DELETE /sessions/:id - удалить (завершить) сессию
|
||||
.delete("/:id", async ({ params, currentUser, status }) => {
|
||||
const { id } = params;
|
||||
|
||||
// Проверить, что сессия существует и принадлежит пользователю
|
||||
const session = await serverSessionService.findByIdForUser(
|
||||
id,
|
||||
currentUser.id
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
return status(404, "Session not found");
|
||||
}
|
||||
|
||||
// Если сессия активна, изменить статус на "ending"
|
||||
if (session.status === "started" || session.status === "starting") {
|
||||
await serverSessionService.end(id);
|
||||
return { message: "Session is ending" };
|
||||
}
|
||||
|
||||
// Если сессия уже завершена или завершается
|
||||
return { message: "Session already ended or ending" };
|
||||
})
|
||||
// POST /sessions/:id/extend - продлить сессию
|
||||
.post(
|
||||
"/:id/extend",
|
||||
async ({ params, body, currentUser, status }) => {
|
||||
const { id } = params;
|
||||
const { minutes } = body as { minutes: number };
|
||||
|
||||
// Проверить, что сессия существует и принадлежит пользователю
|
||||
const session = await serverSessionService.findByIdForUser(
|
||||
id,
|
||||
currentUser.id
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
return status(404, "Session not found");
|
||||
}
|
||||
|
||||
// Проверить, что сессия активна
|
||||
if (session.status !== "started") {
|
||||
return status(400, "Can only extend active sessions");
|
||||
}
|
||||
|
||||
// Продлить сессию
|
||||
try {
|
||||
const updatedSession = await serverSessionService.extend(id, minutes);
|
||||
return { session: updatedSession };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return status(400, error.message);
|
||||
}
|
||||
return status(500, "Failed to extend session");
|
||||
}
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
minutes: t.Number({ minimum: 1, maximum: 120 }),
|
||||
}),
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user