Добавил модераторскую роль
This commit is contained in:
parent
fb6fb58d11
commit
3e1cfefdd9
15 changed files with 141 additions and 48 deletions
|
|
@ -1,9 +1,12 @@
|
|||
---
|
||||
import type { users } from "@prisma/client";
|
||||
|
||||
export interface Props {
|
||||
is_user_admin: boolean;
|
||||
user: users;
|
||||
}
|
||||
|
||||
const { is_user_admin } = Astro.props;
|
||||
const { is_user_admin, user } = Astro.props;
|
||||
|
||||
const items = [
|
||||
{
|
||||
|
|
@ -25,6 +28,10 @@ const items = [
|
|||
];
|
||||
|
||||
const itemsRight = [
|
||||
{
|
||||
href: `/user/${user.login}`,
|
||||
title: "Профиль",
|
||||
},
|
||||
{
|
||||
href: "/logout",
|
||||
title: "Выйти",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const formatDate = (dt: Date) => {
|
|||
---
|
||||
|
||||
<div class="container">
|
||||
{user.is_admin ? (
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<a class="btn btn-sm btn-primary d-block mt-2" href="/articleEditor">
|
||||
Новая статья
|
||||
</a>
|
||||
|
|
@ -35,7 +35,7 @@ const formatDate = (dt: Date) => {
|
|||
<h4 class="alert-heading">{article.title}</h4>
|
||||
<h6>{formatDate(article.created_at)}</h6>
|
||||
<p data-article={article.message} />
|
||||
{user.is_admin ? (
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<hr />
|
||||
<div class="d-flex gap-3">
|
||||
<button class="btn btn-sm btn-danger flex-grow-1" onclick={`deleteArticle(${article.id})`}>
|
||||
|
|
@ -58,7 +58,7 @@ const formatDate = (dt: Date) => {
|
|||
<h4 class="alert-heading">{article.title}</h4>
|
||||
<h6>{formatDate(article.created_at)}</h6>
|
||||
<p data-article={article.message} />
|
||||
{user.is_admin ? (
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<hr />
|
||||
<div class="d-flex gap-3">
|
||||
<button class="btn btn-sm btn-danger flex-grow-1" onclick={`deleteArticle(${article.id})`}>
|
||||
|
|
@ -79,7 +79,7 @@ const formatDate = (dt: Date) => {
|
|||
</div>
|
||||
}
|
||||
</div>
|
||||
{user.is_admin ?
|
||||
{user.is_admin || user.is_moderator ?
|
||||
<script is:inline>
|
||||
async function deleteArticle(articleId) {
|
||||
try {
|
||||
|
|
|
|||
12
src/db.ts
12
src/db.ts
|
|
@ -113,6 +113,18 @@ export async function updateUserLore(login: string, newLore: string) {
|
|||
return user;
|
||||
}
|
||||
|
||||
export async function makeUserModerator(login: string) {
|
||||
const user = await client.users.update({
|
||||
where: {
|
||||
login: login,
|
||||
},
|
||||
data: {
|
||||
is_moderator: true,
|
||||
},
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function deleteUser(login: string) {
|
||||
const user = await client.users.delete({
|
||||
where: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { news } from "@prisma/client";
|
|||
const sessId = Astro.cookies.get("session").value!;
|
||||
const user = await getSessionUser(sessId);
|
||||
|
||||
if (user === null || !user.is_admin) {
|
||||
if (user === null || (!user.is_admin && !user.is_moderator)) {
|
||||
return Astro.redirect("/login");
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ if (articleId) {
|
|||
|
||||
<Layout title="Создание новости">
|
||||
<main data-article={article?.message} data-articleId={articleId}>
|
||||
<Navbar is_user_admin={user.is_admin} />
|
||||
<Navbar is_user_admin={user.is_admin} user={user} />
|
||||
<div class="container mt-5" style="max-width: 650px;">
|
||||
<div class="d-flex">
|
||||
<button type="button" class="btn btn-sm btn-success flex-fill" id="saveBtn">Сохранить</button>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { createArticle, updateArticle } from "../../db";
|
||||
import { createArticle, updateArticle, getSessionUser } from "../../db";
|
||||
|
||||
export async function post({ request, url }: APIContext) {
|
||||
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 && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const fd = await request.formData();
|
||||
const title = fd.get("title");
|
||||
const data = fd.get("data");
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { deleteArticle } from "../../db";
|
||||
import { deleteArticle, getSessionUser } from "../../db";
|
||||
|
||||
export async function post({ request, url }: APIContext) {
|
||||
export async function post({ request, url, 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 && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const postId = parseInt(url.searchParams.get("id") ?? "");
|
||||
if (!postId) {
|
||||
throw new Error("Неправильный формат запроса");
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ const user = (await getSessionUser(sessId))!;
|
|||
|
||||
<Layout title="Пользователи">
|
||||
<main>
|
||||
<Navbar is_user_admin={user.is_admin} />
|
||||
{user.is_admin && <StudyItemsList />}
|
||||
{user.is_admin && <TimetableEditor />}
|
||||
{!user.is_admin && <TimetableViewer user={user} />}
|
||||
<Navbar is_user_admin={user.is_admin} user={user} />
|
||||
{(user.is_admin || user.is_moderator) && <StudyItemsList />}
|
||||
{(user.is_admin || user.is_moderator) && <TimetableEditor />}
|
||||
{!user.is_admin && !user.is_moderator && <TimetableViewer user={user} />}
|
||||
</main>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export async function post({ request, cookies }: APIContext) {
|
|||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin) {
|
||||
throw new Error("Доступно только администраторам");
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export async function post({ request, cookies }: APIContext) {
|
|||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin) {
|
||||
throw new Error("Доступно только администраторам");
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ export async function post({ request, cookies }: APIContext) {
|
|||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin) {
|
||||
throw new Error("Доступно только администраторам");
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const data: UpdateTTModel = await request.json();
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const isCurrentUser = user.id === openedUser.id;
|
|||
|
||||
<Layout title={`Пользователь ${openedUser.fullName ?? openedUser.login}`}>
|
||||
<main data-lore={openedUser.lore} data-login={openedUser.login}>
|
||||
<Navbar is_user_admin={user.is_admin} />
|
||||
<Navbar is_user_admin={user.is_admin} user={user} />
|
||||
<div class="container mt-4 d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label for="login" class="form-label">Логин</label>
|
||||
|
|
@ -42,13 +42,13 @@ const isCurrentUser = user.id === openedUser.id;
|
|||
{
|
||||
isCurrentUser ? (
|
||||
<div class="d-flex flex-row gap-4">
|
||||
<input type="text" class="form-control" id="fullName" name="fullName" value={openedUser.fullName ?? "Не установлено"} />
|
||||
<input type="text" class="form-control" id="fullName" name="fullName" value={openedUser.fullName ?? ""} placeholder="Не установлено" />
|
||||
<button class="btn btn-warning" onclick={`changeName("${user.login}")`}>
|
||||
Изменить
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<input type="email" class="form-control" id="fullName" name="fullName" value={openedUser.fullName ?? "Не установлено"} readonly />
|
||||
<input type="email" class="form-control" id="fullName" name="fullName" value={openedUser.fullName ?? ""} placeholder="Не установлено" readonly />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
42
src/pages/userapi/makeUserModerator.ts
Normal file
42
src/pages/userapi/makeUserModerator.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { makeUserModerator, 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("Не предоставлены данные для наделения полномочиями модератора");
|
||||
}
|
||||
|
||||
const updatedUser = await makeUserModerator(login.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),
|
||||
};
|
||||
}
|
||||
|
|
@ -9,8 +9,8 @@ export async function post({ request, cookies }: APIContext) {
|
|||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin) {
|
||||
throw new Error("Доступно только администраторам");
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
|
|
|
|||
|
|
@ -20,17 +20,16 @@ const user = (await getSessionUser(sessId))!;
|
|||
|
||||
const sLogin = Astro.url.searchParams.get("login");
|
||||
const sFullName = Astro.url.searchParams.get("fullName");
|
||||
const sIsAdmin = Astro.url.searchParams.get("isAdmin");
|
||||
const users = await searchUsers({
|
||||
login: sLogin ? sLogin : undefined,
|
||||
fullName: sFullName ? sFullName : undefined,
|
||||
isAdmin: sIsAdmin ? sIsAdmin === "isAdmin" : undefined,
|
||||
isAdmin: undefined,
|
||||
});
|
||||
---
|
||||
|
||||
<Layout title="Пользователи">
|
||||
<main>
|
||||
<Navbar is_user_admin={user.is_admin} />
|
||||
<Navbar is_user_admin={user.is_admin} user={user} />
|
||||
<div class="container mt-4 d-flex flex-column gap-4">
|
||||
<form class="mb-4" method="GET" action="/users">
|
||||
<div class="mb-2">
|
||||
|
|
@ -41,21 +40,11 @@ const users = await searchUsers({
|
|||
<label for="fullName" class="form-label">Ф.И.О.</label>
|
||||
<input type="text" class="form-control form-control-sm" name="fullName" id="fullName" value={Astro.url.searchParams.get("fullName")} />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
{
|
||||
sIsAdmin === "isAdmin" ? (
|
||||
<input class="form-check-input" type="checkbox" value="isAdmin" name="isAdmin" id="isAdmin" checked />
|
||||
) : (
|
||||
<input class="form-check-input" type="checkbox" value="isAdmin" name="isAdmin" id="isAdmin" />
|
||||
)
|
||||
}
|
||||
<label class="form-check-label" for="isAdmin">Администратор</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-sm btn-warning w-100">Найти</button>
|
||||
</form>
|
||||
{
|
||||
users.map((e) => (
|
||||
<div class="card flex-grow-1">
|
||||
<div class="card flex-grow-1 border border-5">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{e.fullName}</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">{e.login}</h6>
|
||||
|
|
@ -63,17 +52,24 @@ const users = await searchUsers({
|
|||
<a href={`/user/${e.login}`} class="btn btn-primary btn-sm">
|
||||
Открыть профиль
|
||||
</a>
|
||||
{user.is_admin ? (
|
||||
{(user.is_admin || user.is_moderator) && !e.is_admin ? (
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick={`doChangePassword("${e.login}")`}>
|
||||
Изменить пароль
|
||||
</button>
|
||||
<a href={`/timetable?userId=${e.id}`} class="btn btn-primary btn-sm">
|
||||
<a href={`/timetable?login=${e.login}`} class="btn btn-primary btn-sm">
|
||||
Редактировать расписание
|
||||
</a>
|
||||
) : null}
|
||||
{user.is_admin && e.id !== user.id && !e.is_admin ? (
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick={`doDeleteUser("${e.login}")`}>
|
||||
Удалить пользователя
|
||||
</button>
|
||||
) : null}
|
||||
{user.is_admin && !e.is_admin && !e.is_moderator ? (
|
||||
<button type="button" class="btn btn-warning btn-sm" onclick={`doMakeModerator("${e.login}")`}>
|
||||
Назначить модератором
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -111,6 +107,29 @@ const users = await searchUsers({
|
|||
}
|
||||
}
|
||||
}
|
||||
async function doMakeModerator(login) {
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.append("login", login);
|
||||
const resp = await fetch("/userapi/makeUserModerator", {
|
||||
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}' для подтверждения`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue