Добавил возможность удалять пользователей и менять им пароли
This commit is contained in:
parent
a287fec381
commit
8a7231cdd2
5 changed files with 239 additions and 0 deletions
|
|
@ -56,6 +56,7 @@ model users {
|
||||||
id Int @id(map: "pk_users") @default(autoincrement())
|
id Int @id(map: "pk_users") @default(autoincrement())
|
||||||
login String @unique @db.VarChar(25)
|
login String @unique @db.VarChar(25)
|
||||||
pass String @db.VarChar(100)
|
pass String @db.VarChar(100)
|
||||||
|
fullName String? @db.VarChar(100)
|
||||||
is_admin Boolean @default(false)
|
is_admin Boolean @default(false)
|
||||||
timetable timetable[]
|
timetable timetable[]
|
||||||
user_session user_session[]
|
user_session user_session[]
|
||||||
|
|
|
||||||
37
src/db.ts
37
src/db.ts
|
|
@ -68,6 +68,27 @@ export async function createUser(login: string, password: string) {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateUserPassword(login: string, newPassword: string) {
|
||||||
|
const user = await client.users.update({
|
||||||
|
where: {
|
||||||
|
login: login,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
pass: toHex(blake3(newPassword)),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUser(login: string) {
|
||||||
|
const user = await client.users.delete({
|
||||||
|
where: {
|
||||||
|
login: login,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
export async function createSession(user: users) {
|
export async function createSession(user: users) {
|
||||||
const session = await client.user_session.create({
|
const session = await client.user_session.create({
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -144,3 +165,19 @@ export async function getArticle(articleId: number) {
|
||||||
|
|
||||||
return article;
|
return article;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function searchUsers(params: { login?: string; isAdmin?: boolean; fullName?: string }) {
|
||||||
|
const users = await client.users.findMany({
|
||||||
|
where: {
|
||||||
|
login: {
|
||||||
|
contains: params.login,
|
||||||
|
},
|
||||||
|
is_admin: params.isAdmin,
|
||||||
|
fullName: params.fullName,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
|
||||||
46
src/pages/userapi/deleteUser.ts
Normal file
46
src/pages/userapi/deleteUser.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import type { APIContext } from "astro";
|
||||||
|
import { deleteUser, getSessionUser } from "../../db";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
|
export async function post({ request, cookies }: APIContext) {
|
||||||
|
const response: { ok: boolean; reason?: string } = {
|
||||||
|
ok: true,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const sessId = cookies.get("session").value!;
|
||||||
|
const user = (await getSessionUser(sessId))!;
|
||||||
|
if (!user.is_admin) {
|
||||||
|
throw new Error("Доступно только администраторам");
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
const login = formData.get("login");
|
||||||
|
if (login === null) {
|
||||||
|
throw new Error("Не предоставлены данные для удаления пользователя");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (login.toString() === user.login) {
|
||||||
|
throw new Error("Невозможно удалить самого себя");
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedUser = await deleteUser(login.toString());
|
||||||
|
if (deletedUser === null) {
|
||||||
|
throw new Error("Не удалось удалить пользователя");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ok = true;
|
||||||
|
} catch (e: any) {
|
||||||
|
response.ok = false;
|
||||||
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
response.reason = `Неизвестная ошибка базы данных. Код ${e.code}`;
|
||||||
|
} else if (e instanceof Error) {
|
||||||
|
response.reason = e.message.trim();
|
||||||
|
} else {
|
||||||
|
response.reason = e.toString().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
body: JSON.stringify(response),
|
||||||
|
};
|
||||||
|
}
|
||||||
43
src/pages/userapi/updatePassword.ts
Normal file
43
src/pages/userapi/updatePassword.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import type { APIContext } from "astro";
|
||||||
|
import { updateUserPassword, getSessionUser } from "../../db";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
|
export async function post({ request, cookies }: APIContext) {
|
||||||
|
const response: { ok: boolean; reason?: string } = {
|
||||||
|
ok: true,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const sessId = cookies.get("session").value!;
|
||||||
|
const user = (await getSessionUser(sessId))!;
|
||||||
|
if (!user.is_admin) {
|
||||||
|
throw new Error("Доступно только администраторам");
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
const login = formData.get("login");
|
||||||
|
const password = formData.get("password");
|
||||||
|
if (login === null || password === null) {
|
||||||
|
throw new Error("Не предоставлены данные для обновления пароля");
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedUser = await updateUserPassword(login.toString(), password.toString());
|
||||||
|
if (updatedUser === null) {
|
||||||
|
throw new Error("Не удалось обновить пароль");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ok = true;
|
||||||
|
} catch (e: any) {
|
||||||
|
response.ok = false;
|
||||||
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
response.reason = `Неизвестная ошибка базы данных. Код ${e.code}`;
|
||||||
|
} else if (e instanceof Error) {
|
||||||
|
response.reason = e.message.trim();
|
||||||
|
} else {
|
||||||
|
response.reason = e.toString().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
body: JSON.stringify(response),
|
||||||
|
};
|
||||||
|
}
|
||||||
112
src/pages/users.astro
Normal file
112
src/pages/users.astro
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
---
|
||||||
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
|
||||||
|
import { getUserSession, searchUsers, getSessionUser } from "../db";
|
||||||
|
import Navbar from "../components/Navbar.astro";
|
||||||
|
|
||||||
|
if (Astro.cookies.has("session")) {
|
||||||
|
const sessId = Astro.cookies.get("session").value!;
|
||||||
|
const dbSess = await getUserSession(sessId);
|
||||||
|
if (dbSess === null) {
|
||||||
|
Astro.cookies.delete("session");
|
||||||
|
return Astro.redirect("/login");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Astro.redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessId = Astro.cookies.get("session").value!;
|
||||||
|
const user = (await getSessionUser(sessId))!;
|
||||||
|
if (!user.is_admin) {
|
||||||
|
return Astro.redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = await searchUsers({});
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title="Пользователи">
|
||||||
|
<main>
|
||||||
|
<Navbar is_user_admin={user.is_admin} />
|
||||||
|
<div class="container mt-4 d-flex flex-column gap-4">
|
||||||
|
{
|
||||||
|
users.map((e) => (
|
||||||
|
<div class="card flex-grow-1">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{e.fullName}</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">{e.login}</h6>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" onclick={`doChangePassword("${e.login}")`}>
|
||||||
|
Изменить пароль
|
||||||
|
</button>
|
||||||
|
<a href={`/user/${e.id}`} class="btn btn-primary btn-sm">
|
||||||
|
Открыть профиль
|
||||||
|
</a>
|
||||||
|
<a href={`/timetable?userId=${e.id}`} class="btn btn-primary btn-sm">
|
||||||
|
Редактировать расписание
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm" onclick={`doDeleteUser("${e.login}")`}>
|
||||||
|
Удалить пользователя
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
async function doChangePassword(login) {
|
||||||
|
const newPassword = prompt("Введите новый пароль пользователя");
|
||||||
|
if (newPassword === null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("login", login);
|
||||||
|
fd.append("password", newPassword);
|
||||||
|
const resp = await fetch("/userapi/updatePassword", {
|
||||||
|
method: "POST",
|
||||||
|
body: fd,
|
||||||
|
});
|
||||||
|
const json = await resp.json();
|
||||||
|
if (json.ok) {
|
||||||
|
alert("Успех");
|
||||||
|
} else {
|
||||||
|
throw new Error(json.reason);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e instanceof Error) {
|
||||||
|
alert(e.message);
|
||||||
|
} else {
|
||||||
|
alert("Неизвестная ошибка");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doDeleteUser(login) {
|
||||||
|
const confirmLogin = prompt(`Это действие невозможно отменить. \nВведите логин пользователя '${login}' для подтверждения`);
|
||||||
|
if (confirmLogin !== login) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("login", login);
|
||||||
|
const resp = await fetch("/userapi/deleteUser", {
|
||||||
|
method: "POST",
|
||||||
|
body: fd,
|
||||||
|
});
|
||||||
|
const json = await resp.json();
|
||||||
|
if (json.ok) {
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
throw new Error(json.reason);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e instanceof Error) {
|
||||||
|
alert(e.message);
|
||||||
|
} else {
|
||||||
|
alert("Неизвестная ошибка");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue