feat: support ban user and set admin permission action

This commit is contained in:
Zhang Minghan 2024-02-11 13:12:30 +08:00
parent 4fa85b1f12
commit e2ff065218
10 changed files with 223 additions and 4 deletions

View File

@ -29,6 +29,16 @@ type EmailMigrationForm struct {
Email string `json:"email"`
}
type SetAdminForm struct {
Id int64 `json:"id"`
Admin bool `json:"admin"`
}
type BanForm struct {
Id int64 `json:"id"`
Ban bool `json:"ban"`
}
type QuotaOperationForm struct {
Id int64 `json:"id" binding:"required"`
Quota float32 `json:"quota" binding:"required"`
@ -220,6 +230,58 @@ func UpdateEmailAPI(c *gin.Context) {
})
}
func SetAdminAPI(c *gin.Context) {
db := utils.GetDBFromContext(c)
var form SetAdminForm
if err := c.ShouldBindJSON(&form); err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": err.Error(),
})
return
}
err := setAdmin(db, form.Id, form.Admin)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": true,
})
}
func BanAPI(c *gin.Context) {
db := utils.GetDBFromContext(c)
var form BanForm
if err := c.ShouldBindJSON(&form); err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": err.Error(),
})
return
}
err := banUser(db, form.Id, form.Ban)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": true,
})
}
func UserQuotaAPI(c *gin.Context) {
db := utils.GetDBFromContext(c)

View File

@ -28,6 +28,8 @@ func Register(app *gin.RouterGroup) {
app.POST("/admin/user/release", ReleaseUsageAPI)
app.POST("/admin/user/password", UpdatePasswordAPI)
app.POST("/admin/user/email", UpdateEmailAPI)
app.POST("/admin/user/ban", BanAPI)
app.POST("/admin/user/admin", SetAdminAPI)
app.POST("/admin/user/root", UpdateRootPasswordAPI)
app.POST("/admin/market/update", UpdateMarketAPI)

View File

@ -133,6 +133,22 @@ func emailMigration(db *sql.DB, id int64, email string) error {
return err
}
func setAdmin(db *sql.DB, id int64, isAdmin bool) error {
_, err := db.Exec(`
UPDATE auth SET is_admin = ? WHERE id = ?
`, isAdmin, id)
return err
}
func banUser(db *sql.DB, id int64, isBanned bool) error {
_, err := db.Exec(`
UPDATE auth SET is_banned = ? WHERE id = ?
`, isBanned, id)
return err
}
func quotaMigration(db *sql.DB, id int64, quota float32, override bool) error {
// if quota is negative, then decrease quota
// if quota is positive, then increase quota

View File

@ -225,6 +225,36 @@ export async function subscriptionOperation(
}
}
export async function banUserOperation(
id: number,
ban: boolean,
): Promise<CommonResponse> {
try {
const response = await axios.post("/admin/user/ban", {
id,
ban,
});
return response.data as CommonResponse;
} catch (e) {
return { status: false, message: getErrorMessage(e) };
}
}
export async function setAdminOperation(
id: number,
admin: boolean,
): Promise<CommonResponse> {
try {
const response = await axios.post("/admin/user/admin", {
id,
admin,
});
return response.data as CommonResponse;
} catch (e) {
return { status: false, message: getErrorMessage(e) };
}
}
export async function subscriptionLevelOperation(
id: number,
level: number,

View File

@ -23,12 +23,13 @@ export enum popupTypes {
export type PopupDialogProps = {
title: string;
description?: string;
name: string;
name?: string;
placeholder?: string;
defaultValue?: string;
onValueChange?: (value: string) => string;
onSubmit?: (value: string) => Promise<boolean>;
destructive?: boolean;
disabled?: boolean;
type?: popupTypes;
@ -112,6 +113,7 @@ function PopupDialog(props: PopupDialogProps) {
cancelLabel,
confirmLabel,
destructive,
disabled,
} = props;
const { t } = useTranslation();
@ -137,6 +139,7 @@ function PopupDialog(props: PopupDialogProps) {
{cancelLabel || t("cancel")}
</Button>
<Button
disabled={disabled}
variant={destructive ? `destructive` : `default`}
loading={true}
onClick={async () => {

View File

@ -8,9 +8,11 @@ import {
UserResponse,
} from "@/admin/types.ts";
import {
banUserOperation,
getUserList,
quotaOperation,
releaseUsageOperation,
setAdminOperation,
subscriptionLevelOperation,
subscriptionOperation,
updateEmail,
@ -43,9 +45,13 @@ import {
KeyRound,
Loader2,
Mail,
MinusCircle,
MoreHorizontal,
PlusCircle,
RotateCw,
Search,
Shield,
ShieldMinus,
} from "lucide-react";
import { Input } from "@/components/ui/input.tsx";
import PopupDialog, { popupTypes } from "@/components/PopupDialog.tsx";
@ -87,6 +93,8 @@ function OperationMenu({ user, onRefresh }: OperationMenuProps) {
const [subscriptionLevelOpen, setSubscriptionLevelOpen] =
useState<boolean>(false);
const [releaseOpen, setReleaseOpen] = useState<boolean>(false);
const [banOpen, setBanOpen] = useState<boolean>(false);
const [adminOpen, setAdminOpen] = useState<boolean>(false);
return (
<>
@ -217,6 +225,50 @@ function OperationMenu({ user, onRefresh }: OperationMenuProps) {
return resp.status;
}}
/>
<PopupDialog
disabled={username === user.username}
destructive={true}
type={popupTypes.Empty}
title={user.is_banned ? t("admin.unban-action") : t("admin.ban-action")}
description={
user.is_banned
? t("admin.unban-action-desc")
: t("admin.ban-action-desc")
}
open={banOpen}
setOpen={setBanOpen}
onSubmit={async () => {
const resp = await banUserOperation(user.id, !user.is_banned);
doToast(t, toast, resp);
if (resp.status) onRefresh?.();
return resp.status;
}}
/>
<PopupDialog
disabled={username === user.username}
destructive={true}
type={popupTypes.Empty}
title={
user.is_admin
? t("admin.cancel-admin-action")
: t("admin.set-admin-action")
}
description={
user.is_admin
? t("admin.cancel-admin-action-desc")
: t("admin.set-admin-action-desc")
}
open={adminOpen}
setOpen={setAdminOpen}
onSubmit={async () => {
const resp = await setAdminOperation(user.id, !user.is_admin);
doToast(t, toast, resp);
if (resp.status) onRefresh?.();
return resp.status;
}}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -233,6 +285,28 @@ function OperationMenu({ user, onRefresh }: OperationMenuProps) {
<Mail className={`h-4 w-4 mr-2`} />
{t("admin.email-action")}
</DropdownMenuItem>
{user.is_banned ? (
<DropdownMenuItem onClick={() => setBanOpen(true)}>
<PlusCircle className={`h-4 w-4 mr-2`} />
{t("admin.unban-action")}
</DropdownMenuItem>
) : (
<DropdownMenuItem onClick={() => setBanOpen(true)}>
<MinusCircle className={`h-4 w-4 mr-2`} />
{t("admin.ban-action")}
</DropdownMenuItem>
)}
{user.is_admin ? (
<DropdownMenuItem onClick={() => setAdminOpen(true)}>
<ShieldMinus className={`h-4 w-4 mr-2`} />
{t("admin.cancel-admin-action")}
</DropdownMenuItem>
) : (
<DropdownMenuItem onClick={() => setAdminOpen(true)}>
<Shield className={`h-4 w-4 mr-2`} />
{t("admin.set-admin-action")}
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => setQuotaOpen(true)}>
<CloudFog className={`h-4 w-4 mr-2`} />
{t("admin.quota-action")}

View File

@ -433,6 +433,14 @@
"search-username": "搜索用户名",
"password-action": "修改密码",
"password-action-desc": "请输入用户的新密码",
"set-admin-action": "设为管理员",
"set-admin-action-desc": "确定将该用户设为管理员?",
"cancel-admin-action": "取消管理员",
"cancel-admin-action-desc": "确定取消该用户的管理员权限?",
"ban-action": "封禁用户",
"ban-action-desc": "确定将该用户封禁?",
"unban-action": "解封用户",
"unban-action-desc": "确定将该用户解封?",
"email-action": "修改邮箱",
"email-action-desc": "请输入用户的新邮箱",
"quota-action": "点数变更",

View File

@ -605,7 +605,15 @@
"email-action": "Modify email",
"email-action-desc": "Please enter the user's new email",
"default-password": "Password change prompt",
"default-password-prompt": "Your administrator password is the default password. For the security of your account, please change your password as soon as possible. (Go to Back Office - System Settings - Change Root Password)"
"default-password-prompt": "Your administrator password is the default password. For the security of your account, please change your password as soon as possible. (Go to Back Office - System Settings - Change Root Password)",
"set-admin-action": "Set as adminisitrator",
"set-admin-action-desc": "Are you sure you want to make this user an admin?",
"cancel-admin-action": "Cancel the admin",
"cancel-admin-action-desc": "Are you sure you want to remove admin access for this user?",
"ban-action": "Ban User",
"ban-action-desc": "Are you sure you want to ban this user?",
"unban-action": " unBlock User",
"unban-action-desc": "Are you sure you want to unblock this user?"
},
"mask": {
"title": "Mask Settings",

View File

@ -605,7 +605,15 @@
"email-action": "メールアドレスを変更する",
"email-action-desc": "ユーザーの新しいメールアドレスを入力してください",
"default-password": "パスワード変更プロンプト",
"default-password-prompt": "管理者パスワードはデフォルトのパスワードです。アカウントのセキュリティのため、できるだけ早くパスワードを変更してください。(バックオフィスに移動-システム設定-ルートパスワードの変更)"
"default-password-prompt": "管理者パスワードはデフォルトのパスワードです。アカウントのセキュリティのため、できるだけ早くパスワードを変更してください。(バックオフィスに移動-システム設定-ルートパスワードの変更)",
"set-admin-action": "管理者にする",
"set-admin-action-desc": "このユーザーを管理者にしてもよろしいですか?",
"cancel-admin-action": "管理者をキャンセル",
"cancel-admin-action-desc": "このユーザーの管理者アクセスを削除してもよろしいですか?",
"ban-action": "ゴーストユーザー",
"ban-action-desc": "このユーザーを禁止してもよろしいですか?",
"unban-action": "ユーザーのブロックを解除する",
"unban-action-desc": "このユーザーのブロックを解除してもよろしいですか?"
},
"mask": {
"title": "プリセット設定",

View File

@ -605,7 +605,15 @@
"email-action": "Изменение почтового ящика",
"email-action-desc": "Введите новый адрес электронной почты пользователя",
"default-password": "Запрос на изменение пароля",
"default-password-prompt": "Пароль администратора является паролем по умолчанию. В целях безопасности вашей учетной записи, пожалуйста, измените пароль как можно скорее. (Перейдите в Back Office - System Settings - Change Root Password)"
"default-password-prompt": "Пароль администратора является паролем по умолчанию. В целях безопасности вашей учетной записи, пожалуйста, измените пароль как можно скорее. (Перейдите в Back Office - System Settings - Change Root Password)",
"set-admin-action": "Сделать администратором",
"set-admin-action-desc": "Вы уверены, что хотите сделать этого пользователя администратором?",
"cancel-admin-action": "Отменить администрирование",
"cancel-admin-action-desc": "Вы уверены, что хотите удалить доступ администратора для этого пользователя?",
"ban-action": "Пользователь-призрак",
"ban-action-desc": "Вы уверены, что хотите заблокировать этого пользователя?",
"unban-action": "Разблокировать пользователя",
"unban-action-desc": "Вы уверены, что хотите разблокировать этого пользователя?"
},
"mask": {
"title": "Настройки маски",