@ddj-v2/user-management 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +21 -0
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/asset/fig1.png +0 -0
- package/asset/fig2.png +0 -0
- package/index.ts +342 -0
- package/package.json +25 -0
- package/pages/user_manage_detail.page.js +268 -0
- package/pages/user_manage_main.page.js +200 -0
- package/templates/user_manage_detail.html +286 -0
- package/templates/user_manage_main.html +184 -0
package/.npmignore
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 排除所有隱藏檔案與資料夾
|
|
2
|
+
.*
|
|
3
|
+
!/.npmignore
|
|
4
|
+
|
|
5
|
+
# 排除 CI 與開發工具設定
|
|
6
|
+
.github/
|
|
7
|
+
.vscode/
|
|
8
|
+
node_modules/
|
|
9
|
+
*.log
|
|
10
|
+
|
|
11
|
+
# 排除測試與原始碼 (如果你的 build 結果在 dist/ 或 lib/)
|
|
12
|
+
test/
|
|
13
|
+
tests/
|
|
14
|
+
src/
|
|
15
|
+
__tests__/
|
|
16
|
+
jest.config.js
|
|
17
|
+
tsconfig.json
|
|
18
|
+
|
|
19
|
+
# 排除 semantic-release 可能產生的檔案
|
|
20
|
+
release.config.js
|
|
21
|
+
.releaserc
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 oRangeSumMer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# HydroOJ插件 用户管理面板
|
|
2
|
+
|
|
3
|
+
一个为 HydroOJ 提供可视化用户管理功能的插件,允许管理员在控制面板中方便地管理用户信息、权限和状态。
|
|
4
|
+
|
|
5
|
+
> 代码很简单,佛系不定期更新~
|
|
6
|
+
>
|
|
7
|
+
> 如果认为好用请给我点一个star,不胜感激。
|
|
8
|
+
|
|
9
|
+
## 安装方法
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
sudo su
|
|
13
|
+
cd /root/.hydro/
|
|
14
|
+
git clone https://github.com/SummerofOrange/hydrooj-user-management
|
|
15
|
+
hydrooj addon add /root/.hydro/hydrooj-user-management
|
|
16
|
+
pm2 restart hydrooj
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 使用方法
|
|
20
|
+
|
|
21
|
+
1. **访问用户管理**: 登录 HydroOJ 后,在控制面板侧边栏找到"用户管理"菜单项
|
|
22
|
+
2. **搜索用户**: 在用户列表页面使用搜索框查找特定用户
|
|
23
|
+
3. **编辑用户**: 点击用户列表中的"编辑"按钮进入用户详情页面
|
|
24
|
+
4. **管理权限**: 在用户详情页面的"权限管理"部分设置用户权限
|
|
25
|
+
5. **重置密码**: 在"密码管理"部分为用户重置新密码
|
|
26
|
+
6. **封禁用户**: 在"用户状态"部分封禁或解封用户
|
|
27
|
+
|
|
28
|
+
## 权限说明
|
|
29
|
+
|
|
30
|
+
插件使用以下权限级别:
|
|
31
|
+
|
|
32
|
+
- **-1**: root(超级管理员)
|
|
33
|
+
- **0**: 已封禁用户
|
|
34
|
+
- **4**: 系统保留
|
|
35
|
+
- **8**: 访客用户
|
|
36
|
+
- **16842756**: 默认用户权限
|
|
37
|
+
- **其他值**: 自定义权限
|
|
38
|
+
|
|
39
|
+
## 界面展示
|
|
40
|
+
|
|
41
|
+
### 用户列表页面
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
### 用户详情页面
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
## 安全特性
|
|
50
|
+
|
|
51
|
+
- ✅ 权限验证:只有具有系统管理权限的用户才能访问
|
|
52
|
+
- ✅ 操作确认:重要操作(如重置密码、封禁用户)需要确认
|
|
53
|
+
- ✅ 权限保护:防止非超级管理员修改超级管理员账户
|
|
54
|
+
- ✅ 数据验证:自动验证用户名和邮箱的唯一性
|
|
55
|
+
- ✅ 输入验证:前端和后端双重验证用户输入
|
|
56
|
+
|
|
57
|
+
## 开发说明
|
|
58
|
+
|
|
59
|
+
### 贡献代码
|
|
60
|
+
|
|
61
|
+
欢迎提交 Issue 和 Pull Request 来改进这个插件。
|
|
62
|
+
|
|
63
|
+
## 许可证
|
|
64
|
+
|
|
65
|
+
MIT License
|
|
66
|
+
|
|
67
|
+
## 支持
|
|
68
|
+
|
|
69
|
+
如果您在使用过程中遇到问题,请:
|
|
70
|
+
|
|
71
|
+
1. 查看 [Issues](https://github.com/SummerofOrange/hydrooj-user-management/issues) 页面
|
|
72
|
+
2. 提交新的 Issue 描述您的问题
|
|
73
|
+
3. 联系作者获取技术支持
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
**注意**: 此插件需要 HydroOJ v5.0.0-beta.6 或更高版本。使用前请确保您有足够的系统管理权限。
|
package/asset/fig1.png
ADDED
|
Binary file
|
package/asset/fig2.png
ADDED
|
Binary file
|
package/index.ts
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Context, Handler, param, PRIV, Types, UserModel, DomainModel,
|
|
3
|
+
ValidationError, UserNotFoundError, PermissionError, Time, SystemModel, moment
|
|
4
|
+
} from 'hydrooj';
|
|
5
|
+
|
|
6
|
+
declare module 'hydrooj' {
|
|
7
|
+
interface Collections {
|
|
8
|
+
// 扩展用户集合类型
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 用户管理处理器基类
|
|
13
|
+
class UserManageHandler extends Handler {
|
|
14
|
+
async prepare() {
|
|
15
|
+
// 检查是否有系统管理权限
|
|
16
|
+
this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 用户管理主页面处理器
|
|
21
|
+
class UserManageMainHandler extends UserManageHandler {
|
|
22
|
+
@param('page', Types.PositiveInt, true)
|
|
23
|
+
@param('search', Types.String, true)
|
|
24
|
+
@param('sort', Types.String, true)
|
|
25
|
+
async get(domainId: string, page = 1, search = '', sort = '_id') {
|
|
26
|
+
const limit = 50;
|
|
27
|
+
const query: any = {};
|
|
28
|
+
|
|
29
|
+
// 搜索功能
|
|
30
|
+
if (search) {
|
|
31
|
+
const searchRegex = new RegExp(search, 'i');
|
|
32
|
+
query.$or = [
|
|
33
|
+
{ uname: searchRegex },
|
|
34
|
+
{ mail: searchRegex },
|
|
35
|
+
{ _id: isNaN(+search) ? undefined : +search }
|
|
36
|
+
].filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 排序选项
|
|
40
|
+
const sortOptions: Record<string, any> = {
|
|
41
|
+
'_id': { _id: 1 },
|
|
42
|
+
'uname': { uname: 1 },
|
|
43
|
+
'regat': { regat: -1 },
|
|
44
|
+
'loginat': { loginat: -1 },
|
|
45
|
+
'priv': { priv: -1 }
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const sortQuery = sortOptions[sort] || { _id: 1 };
|
|
49
|
+
|
|
50
|
+
// 获取用户列表
|
|
51
|
+
const [udocs, upcount] = await this.paginate(
|
|
52
|
+
UserModel.getMulti(query).sort(sortQuery),
|
|
53
|
+
page,
|
|
54
|
+
limit
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// 获取用户在当前域的信息
|
|
58
|
+
const duids = udocs.map(udoc => udoc._id);
|
|
59
|
+
const dudocs = await DomainModel.getMultiUserInDomain(domainId, { uid: { $in: duids } }).toArray();
|
|
60
|
+
const dudocMap = Object.fromEntries(dudocs.map(dudoc => [dudoc.uid, dudoc]));
|
|
61
|
+
|
|
62
|
+
this.response.template = 'user_manage_main.html';
|
|
63
|
+
this.response.body = {
|
|
64
|
+
udocs,
|
|
65
|
+
dudocMap,
|
|
66
|
+
page,
|
|
67
|
+
upcount,
|
|
68
|
+
search,
|
|
69
|
+
sort,
|
|
70
|
+
canEdit: true,
|
|
71
|
+
moment
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 用户详情和编辑处理器
|
|
77
|
+
class UserManageDetailHandler extends UserManageHandler {
|
|
78
|
+
@param('uid', Types.Int)
|
|
79
|
+
async get(domainId: string, uid: number) {
|
|
80
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
81
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
82
|
+
|
|
83
|
+
const dudoc = await DomainModel.getDomainUser(domainId, udoc);
|
|
84
|
+
|
|
85
|
+
this.response.template = 'user_manage_detail.html';
|
|
86
|
+
this.response.body = {
|
|
87
|
+
udoc,
|
|
88
|
+
dudoc,
|
|
89
|
+
canEdit: true,
|
|
90
|
+
moment
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@param('uid', Types.Int)
|
|
95
|
+
@param('operation', Types.String)
|
|
96
|
+
async post(domainId: string, uid: number, operation: string) {
|
|
97
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
98
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
99
|
+
|
|
100
|
+
if (operation === 'edit') {
|
|
101
|
+
await this.postEdit(domainId, uid);
|
|
102
|
+
} else if (operation === 'resetPassword') {
|
|
103
|
+
await this.postResetPassword(domainId, uid);
|
|
104
|
+
} else if (operation === 'setPriv') {
|
|
105
|
+
await this.postSetPriv(domainId, uid);
|
|
106
|
+
} else if (operation === 'ban') {
|
|
107
|
+
await this.postBan(domainId, uid);
|
|
108
|
+
} else if (operation === 'unban') {
|
|
109
|
+
await this.postUnban(domainId, uid);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.back();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@param('uid', Types.Int)
|
|
116
|
+
@param('mail', Types.Email, true)
|
|
117
|
+
@param('uname', Types.Username, true)
|
|
118
|
+
@param('school', Types.String, true)
|
|
119
|
+
@param('bio', Types.Content, true)
|
|
120
|
+
async postEdit(domainId: string, uid: number, mail?: string, uname?: string, school?: string, bio?: string) {
|
|
121
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
122
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
123
|
+
|
|
124
|
+
if (mail && mail !== udoc.mail) {
|
|
125
|
+
// 检查邮箱是否已被使用
|
|
126
|
+
const existing = await UserModel.getByEmail(domainId, mail);
|
|
127
|
+
if (existing && existing._id !== uid) {
|
|
128
|
+
throw new ValidationError('mail', 'Email already in use');
|
|
129
|
+
}
|
|
130
|
+
await UserModel.setEmail(uid, mail);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (uname && uname !== udoc.uname) {
|
|
134
|
+
// 检查用户名是否已被使用
|
|
135
|
+
const existing = await UserModel.getByUname(domainId, uname);
|
|
136
|
+
if (existing && existing._id !== uid) {
|
|
137
|
+
throw new ValidationError('uname', 'Username already in use');
|
|
138
|
+
}
|
|
139
|
+
await UserModel.setUname(uid, uname);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const updates: any = {};
|
|
143
|
+
if (school !== undefined) updates.school = school;
|
|
144
|
+
if (bio !== undefined) updates.bio = bio;
|
|
145
|
+
|
|
146
|
+
if (Object.keys(updates).length > 0) {
|
|
147
|
+
await UserModel.setById(uid, updates);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@param('uid', Types.Int)
|
|
152
|
+
@param('password', Types.Password)
|
|
153
|
+
async postResetPassword(domainId: string, uid: number, password: string) {
|
|
154
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
155
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
156
|
+
|
|
157
|
+
// 不允许重置超级管理员密码(除非当前用户也是超级管理员)
|
|
158
|
+
if (udoc.priv === PRIV.PRIV_ALL && this.user.priv !== PRIV.PRIV_ALL) {
|
|
159
|
+
throw new PermissionError('Cannot reset super admin password');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await UserModel.setPassword(uid, password);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@param('uid', Types.Int)
|
|
166
|
+
@param('priv', Types.Int)
|
|
167
|
+
async postSetPriv(domainId: string, uid: number, priv: number) {
|
|
168
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
169
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
170
|
+
|
|
171
|
+
// 不允许修改超级管理员权限(除非当前用户也是超级管理员)
|
|
172
|
+
if ((udoc.priv === PRIV.PRIV_ALL || priv === PRIV.PRIV_ALL) && this.user.priv !== PRIV.PRIV_ALL) {
|
|
173
|
+
throw new PermissionError('Cannot modify super admin privileges');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
await UserModel.setPriv(uid, priv);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@param('uid', Types.Int)
|
|
180
|
+
async postBan(domainId: string, uid: number) {
|
|
181
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
182
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
183
|
+
|
|
184
|
+
// 不允许封禁超级管理员
|
|
185
|
+
if (udoc.priv === PRIV.PRIV_ALL) {
|
|
186
|
+
throw new PermissionError('Cannot ban super admin');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await UserModel.ban(uid, 'Banned by administrator');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@param('uid', Types.Int)
|
|
193
|
+
async postUnban(domainId: string, uid: number) {
|
|
194
|
+
const udoc = await UserModel.getById(domainId, uid);
|
|
195
|
+
if (!udoc) throw new UserNotFoundError(uid);
|
|
196
|
+
|
|
197
|
+
// 恢复为默认权限
|
|
198
|
+
const defaultPriv = await SystemModel.get('default.priv');
|
|
199
|
+
await UserModel.setPriv(uid, defaultPriv);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
export async function apply(ctx: Context) {
|
|
206
|
+
// 注册路由
|
|
207
|
+
ctx.Route('user_manage_main', '/manage/users', UserManageMainHandler, PRIV.PRIV_EDIT_SYSTEM);
|
|
208
|
+
ctx.Route('user_manage_detail', '/manage/users/:uid', UserManageDetailHandler, PRIV.PRIV_EDIT_SYSTEM);
|
|
209
|
+
|
|
210
|
+
// 在控制面板侧边栏添加用户管理菜单项
|
|
211
|
+
ctx.injectUI('ControlPanel', 'user_manage_main', { icon: 'user' });
|
|
212
|
+
|
|
213
|
+
// 添加国际化支持
|
|
214
|
+
ctx.i18n.load('zh', {
|
|
215
|
+
'user_manage_main': '用户管理',
|
|
216
|
+
'user_manage_detail': '用户详情',
|
|
217
|
+
|
|
218
|
+
'User Management': '用户管理',
|
|
219
|
+
'User List': '用户列表',
|
|
220
|
+
'Search Users': '搜索用户',
|
|
221
|
+
'Search by': '搜索方式',
|
|
222
|
+
'Username': '用户名',
|
|
223
|
+
'Email': '邮箱',
|
|
224
|
+
'User ID': '用户ID',
|
|
225
|
+
'Keyword': '关键词',
|
|
226
|
+
'Sort by': '排序方式',
|
|
227
|
+
'Registration Time': '注册时间',
|
|
228
|
+
'Last Login': '最后登录',
|
|
229
|
+
'Privilege': '权限',
|
|
230
|
+
'Order': '顺序',
|
|
231
|
+
'Ascending': '升序',
|
|
232
|
+
'Descending': '降序',
|
|
233
|
+
'Search': '搜索',
|
|
234
|
+
'Clear': '清空',
|
|
235
|
+
'Refresh': '刷新',
|
|
236
|
+
|
|
237
|
+
'Normal User': '普通用户',
|
|
238
|
+
'Admin': '管理员',
|
|
239
|
+
'Banned': '已封禁',
|
|
240
|
+
'Super Admin': '超级管理员',
|
|
241
|
+
'Active': '活跃',
|
|
242
|
+
'Inactive': '不活跃',
|
|
243
|
+
'Actions': '操作',
|
|
244
|
+
'View': '查看',
|
|
245
|
+
'Edit': '编辑',
|
|
246
|
+
'Ban': '封禁',
|
|
247
|
+
'Unban': '解封',
|
|
248
|
+
'Set Privilege': '设置权限',
|
|
249
|
+
'Status': '状态',
|
|
250
|
+
'School': '学校',
|
|
251
|
+
'Bio': '个人简介',
|
|
252
|
+
'Never': '从未',
|
|
253
|
+
'Not set': '未设置',
|
|
254
|
+
'Previous': '上一页',
|
|
255
|
+
'Next': '下一页',
|
|
256
|
+
'Page': '页',
|
|
257
|
+
'of': '共',
|
|
258
|
+
'users': '用户',
|
|
259
|
+
'Total': '总计',
|
|
260
|
+
'Showing': '显示',
|
|
261
|
+
'to': '到',
|
|
262
|
+
'User Details': '用户详情',
|
|
263
|
+
'Basic Information': '基本信息',
|
|
264
|
+
'User Statistics': '用户统计',
|
|
265
|
+
'Privilege Management': '权限管理',
|
|
266
|
+
'Password Management': '密码管理',
|
|
267
|
+
'User Status': '用户状态',
|
|
268
|
+
'Back to List': '返回列表',
|
|
269
|
+
'Save Changes': '保存更改',
|
|
270
|
+
'Cancel': '取消',
|
|
271
|
+
'Reset Password': '重置密码',
|
|
272
|
+
'Current Privilege': '当前权限',
|
|
273
|
+
'Ban User': '封禁用户',
|
|
274
|
+
'Unban User': '解封用户',
|
|
275
|
+
'Copy User ID': '复制用户ID'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
ctx.i18n.load('en', {
|
|
279
|
+
'user_manage_main': 'User Management',
|
|
280
|
+
'user_manage_detail': 'User Detail',
|
|
281
|
+
'user_manage_batch': 'Batch Operations',
|
|
282
|
+
'User Management': 'User Management',
|
|
283
|
+
'User List': 'User List',
|
|
284
|
+
'Search Users': 'Search Users',
|
|
285
|
+
'Search by': 'Search by',
|
|
286
|
+
'Username': 'Username',
|
|
287
|
+
'Email': 'Email',
|
|
288
|
+
'User ID': 'User ID',
|
|
289
|
+
'Keyword': 'Keyword',
|
|
290
|
+
'Sort by': 'Sort by',
|
|
291
|
+
'Registration Time': 'Registration Time',
|
|
292
|
+
'Last Login': 'Last Login',
|
|
293
|
+
'Privilege': 'Privilege',
|
|
294
|
+
'Order': 'Order',
|
|
295
|
+
'Ascending': 'Ascending',
|
|
296
|
+
'Descending': 'Descending',
|
|
297
|
+
'Search': 'Search',
|
|
298
|
+
'Clear': 'Clear',
|
|
299
|
+
'Refresh': 'Refresh',
|
|
300
|
+
'Batch Operations': 'Batch Operations',
|
|
301
|
+
'Export Users': 'Export Users',
|
|
302
|
+
'Normal User': 'Normal User',
|
|
303
|
+
'Admin': 'Admin',
|
|
304
|
+
'Banned': 'Banned',
|
|
305
|
+
'Super Admin': 'Super Admin',
|
|
306
|
+
'Active': 'Active',
|
|
307
|
+
'Inactive': 'Inactive',
|
|
308
|
+
'Actions': 'Actions',
|
|
309
|
+
'View': 'View',
|
|
310
|
+
'Edit': 'Edit',
|
|
311
|
+
'Ban': 'Ban',
|
|
312
|
+
'Unban': 'Unban',
|
|
313
|
+
'Set Privilege': 'Set Privilege',
|
|
314
|
+
'Status': 'Status',
|
|
315
|
+
'School': 'School',
|
|
316
|
+
'Bio': 'Bio',
|
|
317
|
+
'Never': 'Never',
|
|
318
|
+
'Not set': 'Not set',
|
|
319
|
+
'Previous': 'Previous',
|
|
320
|
+
'Next': 'Next',
|
|
321
|
+
'Page': 'Page',
|
|
322
|
+
'of': 'of',
|
|
323
|
+
'users': 'users',
|
|
324
|
+
'Total': 'Total',
|
|
325
|
+
'Showing': 'Showing',
|
|
326
|
+
'to': 'to',
|
|
327
|
+
'User Details': 'User Details',
|
|
328
|
+
'Basic Information': 'Basic Information',
|
|
329
|
+
'User Statistics': 'User Statistics',
|
|
330
|
+
'Privilege Management': 'Privilege Management',
|
|
331
|
+
'Password Management': 'Password Management',
|
|
332
|
+
'User Status': 'User Status',
|
|
333
|
+
'Back to List': 'Back to List',
|
|
334
|
+
'Save Changes': 'Save Changes',
|
|
335
|
+
'Cancel': 'Cancel',
|
|
336
|
+
'Reset Password': 'Reset Password',
|
|
337
|
+
'Current Privilege': 'Current Privilege',
|
|
338
|
+
'Ban User': 'Ban User',
|
|
339
|
+
'Unban User': 'Unban User',
|
|
340
|
+
'Copy User ID': 'Copy User ID'
|
|
341
|
+
});
|
|
342
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ddj-v2/user-management",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Advanced user management plugin for Hydro",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"author": "Grass_cat",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"hydrooj": "*"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"hydrooj": "*"
|
|
13
|
+
},
|
|
14
|
+
"hydro": {
|
|
15
|
+
"addon": true
|
|
16
|
+
},
|
|
17
|
+
"repository": "https://github.com/ddj-v2/user-management",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
20
|
+
"@semantic-release/github": "^12.0.6",
|
|
21
|
+
"@semantic-release/npm": "^13.1.5",
|
|
22
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
23
|
+
"semantic-release": "^25.0.3"
|
|
24
|
+
}
|
|
25
|
+
}
|