@amxts/perms 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kukson777
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/PAWN.md ADDED
@@ -0,0 +1,53 @@
1
+ # Perms for Pawn plugins
2
+
3
+ [English](PAWN.md) | [Русский](PAWN.ru.md)
4
+
5
+ A Pawn plugin needs nothing from Perms to keep working: the module gives each
6
+ player AMX Mod X's flags from his `amxx.*` nodes, so `get_user_flags`,
7
+ `access`, `cmd_access` and `register_concmd` with flags behave as they do with
8
+ `admin.amxx`.
9
+
10
+ To check permission nodes of its own, a plugin writes `#include <perms>`; the
11
+ package ships `include/perms.inc`:
12
+
13
+ ```pawn
14
+ #include <amxmodx>
15
+ #include <perms>
16
+
17
+ public plugin_init()
18
+ {
19
+ register_plugin("My plugin", "1.0", "you");
20
+ perms_declare("myplugin.trail", "A trail behind the player");
21
+ register_clcmd("say /trail", "CmdTrail");
22
+ }
23
+
24
+ public CmdTrail(id)
25
+ {
26
+ if (!perms_has(id, "myplugin.trail"))
27
+ {
28
+ client_print(id, print_chat, "You have no trail");
29
+ return PLUGIN_HANDLED;
30
+ }
31
+
32
+ new group[32];
33
+ perms_get_group(id, group, charsmax(group));
34
+ client_print(id, print_chat, "Your trail, %s", group);
35
+ return PLUGIN_HANDLED;
36
+ }
37
+
38
+ public perms_changed(id)
39
+ {
40
+ // the player's permissions changed: he joined, a command or the editor changed them
41
+ }
42
+ ```
43
+
44
+ | Native | What it does |
45
+ | --- | --- |
46
+ | `bool:perms_has(id, const node[])` | whether the player has the node |
47
+ | `perms_declare(const node[], const description[], bool:byDefault = false)` | declares a node for the web editor; `byDefault` gives it to every player |
48
+ | `perms_get_group(id, out[], len)` | the player's primary group |
49
+ | `bool:perms_in_group(id, const group[])` | whether the player is in a group |
50
+ | `perms_add_group(id, const group[], seconds = 0)` | puts him into a group, for `seconds` or for good |
51
+ | `perms_remove_group(id, const group[])` | takes him out of it |
52
+
53
+ `public perms_changed(id)` is called when a player's permissions change.
package/PAWN.ru.md ADDED
@@ -0,0 +1,53 @@
1
+ # Perms для Pawn-плагинов
2
+
3
+ [English](PAWN.md) | [Русский](PAWN.ru.md)
4
+
5
+ Pawn-плагину ничего не нужно от Perms, чтобы работать: модуль ставит каждому
6
+ игроку флаги AMX Mod X из его узлов `amxx.*`, поэтому `get_user_flags`,
7
+ `access`, `cmd_access` и `register_concmd` с флагами ведут себя так же, как с
8
+ `admin.amxx`.
9
+
10
+ Чтобы проверять свои узлы прав, плагин пишет `#include <perms>`; пакет
11
+ поставляет `include/perms.inc`:
12
+
13
+ ```pawn
14
+ #include <amxmodx>
15
+ #include <perms>
16
+
17
+ public plugin_init()
18
+ {
19
+ register_plugin("My plugin", "1.0", "you");
20
+ perms_declare("myplugin.trail", "A trail behind the player");
21
+ register_clcmd("say /trail", "CmdTrail");
22
+ }
23
+
24
+ public CmdTrail(id)
25
+ {
26
+ if (!perms_has(id, "myplugin.trail"))
27
+ {
28
+ client_print(id, print_chat, "You have no trail");
29
+ return PLUGIN_HANDLED;
30
+ }
31
+
32
+ new group[32];
33
+ perms_get_group(id, group, charsmax(group));
34
+ client_print(id, print_chat, "Your trail, %s", group);
35
+ return PLUGIN_HANDLED;
36
+ }
37
+
38
+ public perms_changed(id)
39
+ {
40
+ // права игрока изменились: он зашёл, их изменила команда или редактор
41
+ }
42
+ ```
43
+
44
+ | Натив | Что делает |
45
+ | --- | --- |
46
+ | `bool:perms_has(id, const node[])` | есть ли у игрока узел |
47
+ | `perms_declare(const node[], const description[], bool:byDefault = false)` | объявляет узел для веб-редактора; `byDefault` даёт его всем игрокам |
48
+ | `perms_get_group(id, out[], len)` | основная группа игрока |
49
+ | `bool:perms_in_group(id, const group[])` | состоит ли игрок в группе |
50
+ | `perms_add_group(id, const group[], seconds = 0)` | добавляет его в группу на `seconds` секунд или навсегда |
51
+ | `perms_remove_group(id, const group[])` | убирает его из группы |
52
+
53
+ `public perms_changed(id)` вызывается, когда права игрока меняются.
package/README.md CHANGED
@@ -1,3 +1,272 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="assets/logo.svg" width="96" alt="Perms">
4
+
5
+ # Perms
6
+
7
+ *Permissions and groups in place of admin.amxx*
8
+
9
+ [![amxts module](https://img.shields.io/badge/amxts-module-3178c6?style=flat-square)](https://amxts.github.io/)
10
+ [![License](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
11
+
12
+ **English** | [Русский](README.ru.md)
13
+
14
+ </div>
15
+
16
+ Permissions and groups for an amxts server, in place of AMX Mod X's `admin.amxx`:
17
+ permission nodes with wildcards and negation, groups with inheritance and
18
+ weights, temporary grants, a file or MySQL, AMX Mod X's admin flags for Pawn
19
+ plugins - and a web editor the server opens with one command.
20
+
21
+ ```ts
22
+ perms.declare({
23
+ "myplugin.kick": { description: "Kick players" },
24
+ "myplugin.help": { description: "Use /help", everyone: true },
25
+ });
26
+
27
+ server.addCommand<KickArgs>("/kick <target>", ({ player, target }) => {
28
+ if (!perms.has(player, "myplugin.kick")) return player.print("You may not kick");
29
+
30
+ target.kick("Kicked");
31
+ });
32
+ ```
33
+
34
+ ## Install
35
+
36
+ ```sh
37
+ npx amxts module add perms
38
+ ```
39
+
40
+ `perms` is a global in your plugins, as the core's API is - no import line.
41
+
42
+ ## Permissions
43
+
44
+ A permission is a **node**: dotted words, the plugin's name first -
45
+ `myplugin.kick`, `shop.discount`, `kz.admin.wipe`.
46
+
47
+ - `kz.admin.*` gives every node under `kz.admin`; `*` gives everything.
48
+ - `-kz.admin.wipe` takes a node away: a negation.
49
+ - The most specific grant wins: `kz.admin.*` with `-kz.admin.wipe` gives all of
50
+ `kz.admin` but `wipe`. Of a node granted and negated in one place, the
51
+ negation wins.
52
+ - A node nothing grants is what its plugin declared: `everyone: true` gives it
53
+ to every player, otherwise nobody has it.
54
+
55
+ ## Groups
56
+
57
+ A group has its permissions, the groups it `inherits`, and a `weight`.
58
+ Every player is in `default`, without being written in.
59
+
60
+ A player's answer for a node comes from the first of these that names it:
61
+
62
+ 1. his own permissions;
63
+ 2. his groups, the highest weight first - each group before what it inherits;
64
+ 3. `default`.
65
+
66
+ His **primary group** is his group of the highest weight - `perms.groupOf(player)`,
67
+ for a chat tag or a scoreboard.
68
+
69
+ A group or a permission may be **temporary**: it stops counting the moment it
70
+ ends, and the file drops it on its next save.
71
+
72
+ ## Players
73
+
74
+ A player is found by his SteamID, then by his IP, then by his name with a
75
+ password - as `users.ini` finds him:
76
+
77
+ | Entry | Who it is |
78
+ | --- | --- |
79
+ | `STEAM_0:1:12345` | the player with that SteamID |
80
+ | `ip:1.2.3.4` | a player from that address - for a game without Steam |
81
+ | `name:Bob` | a player named `Bob` whose `setinfo _pw` is the entry's password |
82
+
83
+ ## The file
84
+
85
+ `perms.yaml` (or `perms.json`) in the configs folder, `addons/amxmodx/configs/` -
86
+ or `configs/<baseDir>/` when the project sets config-core's `baseDir`, as other
87
+ configs are. The commands and the web editor write it, and it is edited by hand
88
+ too; the server console says at start which file it read:
89
+
90
+ ```yaml
91
+ groups:
92
+ default:
93
+ permissions: [kz.top]
94
+ vip:
95
+ weight: 10
96
+ permissions: [amxx.reservation, shop.discount]
97
+ admin:
98
+ weight: 50
99
+ inherits: [vip]
100
+ permissions: [amxx.kick, amxx.ban, "kz.admin.*", "-kz.admin.wipe"]
101
+ users:
102
+ - id: "STEAM_0:1:12345"
103
+ name: Bob
104
+ groups: [admin]
105
+ permissions: [kz.admin.wipe]
106
+ - id: "STEAM_0:0:777"
107
+ groups:
108
+ - { group: vip, until: 2026-12-01T00:00:00Z }
109
+ ```
110
+
111
+ A permission or a group with an end, or for one server, is an object:
112
+ `{ node: kz.vip, until: 2026-12-01T00:00:00Z, server: kz1 }`. Times are UTC.
113
+
114
+ > [!WARNING]
115
+ > A file with an error is said in the server console with its line, and the
116
+ > permissions read before it stay - a typo never leaves the server without admins.
117
+
118
+ ## MySQL
119
+
120
+ Several servers share one set of permissions in MySQL. Give the database's
121
+ address in the server's `addons/amxts/.env`:
122
+
123
+ ```ini
124
+ PERMS_DB=mysql://perms:secret@127.0.0.1:3306/perms
125
+ PERMS_SERVER=kz1
126
+ ```
127
+
128
+ The module makes its tables (`perms_groups`, `perms_grants`, `perms_users`,
129
+ `perms_members`, `perms_revision`) on its first start. A change on one server
130
+ reaches the others within five seconds.
131
+
132
+ `PERMS_SERVER` names this server: a group or a permission given with
133
+ `server: kz1` counts on it alone.
134
+
135
+ ## In code
136
+
137
+ ```ts
138
+ perms.has(player, "myplugin.kick"); // true or false
139
+ perms.groupOf(player); // "admin"
140
+ perms.groupsOf(player); // ["admin", "default"]
141
+ perms.addGroup(player, "vip", { for: 30 * 86400 }); // seconds; permanent without for
142
+ perms.removeGroup(player, "vip");
143
+ perms.set(player, "myplugin.trail", true, { for: 3600 });
144
+ perms.unset(player, "myplugin.trail");
145
+
146
+ perms.addEventListener("change", ({ player }) => {
147
+ if (perms.has(player, "myplugin.trail")) startTrail(player);
148
+ });
149
+ ```
150
+
151
+ `"change"` comes when a player's permissions change: he joins, a command or
152
+ the editor changes them, or one ends.
153
+
154
+ ## Commands
155
+
156
+ In the server console and over rcon:
157
+
158
+ ```
159
+ perms user <player|id> info
160
+ perms user <player|id> group add <group> [30d]
161
+ perms user <player|id> group remove <group>
162
+ perms user <player|id> permission set <node> [true|false] [30d]
163
+ perms user <player|id> permission unset <node>
164
+ perms group <group> create [weight]
165
+ perms group <group> delete | info
166
+ perms group <group> permission set <node> [true|false] [30d]
167
+ perms group <group> permission unset <node>
168
+ perms group <group> parent add|remove <group>
169
+ perms group <group> weight <n>
170
+ perms groups
171
+ perms check <player|id> <node>
172
+ perms nodes [plugin]
173
+ perms editor [close]
174
+ perms reload
175
+ perms import [users.ini]
176
+ ```
177
+
178
+ A player is `#userid`, a part of his name, or an entry's id
179
+ (`STEAM_0:1:12345`). A duration is `45s`, `90m`, `12h`, `30d` or `2w`.
180
+
181
+ In the game a player with `perms.view` runs the commands that only show -
182
+ `groups`, `check`, `nodes`, `user ... info` and `group ... info` - in his
183
+ console or in chat (`/perms groups`); the answer comes to his console.
184
+ Permissions are changed from the server console and in the web editor only.
185
+
186
+ ## The web editor
187
+
188
+ ```
189
+ perms editor
190
+ [perms] Open the editor (an hour): https://amxts.github.io/modules/perms/editor#k3x9q2mw7a
191
+ ```
192
+
193
+ The link opens the server's permissions in the browser: every plugin's nodes
194
+ with their descriptions, a column per group, the groups, the players. **Save**
195
+ sends the changes back; the server applies them within seconds, without a
196
+ restart, and the page says what it did.
197
+
198
+ - The server console opens it; no player can.
199
+ - Whoever has the link can edit until the session ends: after an hour, or
200
+ `perms editor close`. Share it like a password.
201
+ - What goes to the editor: groups, permissions, SteamIDs and names. Passwords and
202
+ the database's address never leave the server.
203
+ - The editor keeps a session for an hour on amxts's service. To keep them on
204
+ your own, run the store from this package's `store/` folder (a Cloudflare
205
+ Worker) and give its address as `PERMS_EDITOR_URL` in `.env`. The link is
206
+ then your store's, and it opens the same page with your store:
207
+
208
+ ```
209
+ [perms] Open the editor (an hour): https://perms.myserver.com/editor#k3x9q2mw7a
210
+ ```
211
+
212
+ ## In place of admin.amxx
213
+
214
+ 1. `npx amxts module add perms`, build and deploy.
215
+ 2. Comment out `admin.amxx` (or `admin_sql.amxx`) in `plugins.ini`. While one
216
+ of them runs, the module leaves the players' flags to it and says so in the
217
+ console.
218
+ 3. In the server console: `perms import users.ini`. Each admin becomes an entry
219
+ with his flags as `amxx.*` permissions of his own. Check one with
220
+ `perms user <name> info`.
221
+
222
+ `users.ini` is not read again; it stays for going back.
223
+
224
+ Pawn plugins work unchanged: the module gives each player AMX Mod X's flags from
225
+ his `amxx.*` nodes, so `get_user_flags`, `access`, `cmd_access`,
226
+ `is_user_admin` and `cmdaccess.ini` see each player's rights. A player with no `amxx.*`
227
+ node gets `amx_default_access` (`z`). The module also keeps `amx_mode`,
228
+ `amx_password_field`, `amx_default_access` and `amx_reloadadmins`, which server
229
+ configs and plugins use.
230
+
231
+ | Node | Flag |
232
+ | --- | --- |
233
+ | `amxx.immunity` | `a` |
234
+ | `amxx.reservation` | `b` |
235
+ | `amxx.kick` | `c` |
236
+ | `amxx.ban` | `d` |
237
+ | `amxx.slay` | `e` |
238
+ | `amxx.map` | `f` |
239
+ | `amxx.cvar` | `g` |
240
+ | `amxx.cfg` | `h` |
241
+ | `amxx.chat` | `i` |
242
+ | `amxx.vote` | `j` |
243
+ | `amxx.password` | `k` |
244
+ | `amxx.rcon` | `l` |
245
+ | `amxx.levelA` ... `amxx.levelH` | `m` ... `t` |
246
+ | `amxx.menu` | `u` |
247
+ | `amxx.banTemp` | `v` |
248
+ | `amxx.admin` | `y` |
249
+
250
+ > [!WARNING]
251
+ > **amx_mode**
252
+ >
253
+ > `amx_mode 2` kicks a player without an entry, as `admin.amxx` does: a private
254
+ > server. `amx_mode 0` gives nobody anything.
255
+
256
+ Pawn plugins can check nodes themselves: [PAWN.md](PAWN.md).
257
+
258
+ ## Options
259
+
260
+ ```ts
261
+ export default defineConfig({
262
+ modules: ["@amxts/perms"],
263
+ perms: {
264
+ file: "perms", // configs/perms.yaml
265
+ editor: "", // the editor's store; PERMS_EDITOR_URL overrides it
266
+ },
267
+ });
268
+ ```
269
+
270
+ ## License
271
+
272
+ MIT
package/README.ru.md ADDED
@@ -0,0 +1,272 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/logo.svg" width="96" alt="Perms">
4
+
5
+ # Perms
6
+
7
+ *Права и группы вместо admin.amxx*
8
+
9
+ [![amxts module](https://img.shields.io/badge/amxts-module-3178c6?style=flat-square)](https://amxts.github.io/)
10
+ [![License](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
11
+
12
+ [English](README.md) | **Русский**
13
+
14
+ </div>
15
+
16
+ Права и группы для сервера на amxts вместо `admin.amxx` из AMX Mod X: узлы прав
17
+ с масками и запретом, группы с наследованием и весом, временные права, файл или
18
+ MySQL, флаги AMX Mod X для Pawn-плагинов - и веб-редактор, который сервер
19
+ открывает одной командой.
20
+
21
+ ```ts
22
+ perms.declare({
23
+ "myplugin.kick": { description: "Кикать игроков" },
24
+ "myplugin.help": { description: "Пользоваться /help", everyone: true },
25
+ });
26
+
27
+ server.addCommand<KickArgs>("/kick <target>", ({ player, target }) => {
28
+ if (!perms.has(player, "myplugin.kick")) return player.print("Тебе нельзя кикать");
29
+
30
+ target.kick("Kicked");
31
+ });
32
+ ```
33
+
34
+ ## Установка
35
+
36
+ ```sh
37
+ npx amxts module add perms
38
+ ```
39
+
40
+ `perms` в плагинах - глобальное имя, как API ядра: строка импорта не нужна.
41
+
42
+ ## Права
43
+
44
+ Право - это **узел**: слова через точку, первым - имя плагина: `myplugin.kick`,
45
+ `shop.discount`, `kz.admin.wipe`.
46
+
47
+ - `kz.admin.*` даёт все узлы под `kz.admin`; `*` даёт всё.
48
+ - `-kz.admin.wipe` отнимает узел: запрет.
49
+ - Побеждает самое точное право: `kz.admin.*` вместе с `-kz.admin.wipe` даёт весь
50
+ `kz.admin`, кроме `wipe`. Если в одном месте узел и выдан, и запрещён,
51
+ побеждает запрет.
52
+ - Узел, который никто не выдал, - такой, каким его объявил плагин:
53
+ `everyone: true` даёт его всем игрокам, иначе он ни у кого.
54
+
55
+ ## Группы
56
+
57
+ У группы есть её права, группы, которые она наследует (`inherits`), и вес
58
+ (`weight`). Каждый игрок состоит в `default`, даже если нигде не записан.
59
+
60
+ Ответ про узел для игрока берётся из первого, что этот узел называет:
61
+
62
+ 1. его личные права;
63
+ 2. его группы, начиная с самой тяжёлой, - каждая раньше того, что она наследует;
64
+ 3. `default`.
65
+
66
+ **Основная группа** игрока - его группа с самым большим весом:
67
+ `perms.groupOf(player)`, для тега в чате или таблицы счёта.
68
+
69
+ Группа или право могут быть **временными**: они перестают действовать в момент
70
+ окончания, а файл убирает их при следующем сохранении.
71
+
72
+ ## Игроки
73
+
74
+ Игрок находится по SteamID, затем по IP, затем по нику с паролем - так же, как
75
+ его находит `users.ini`:
76
+
77
+ | Запись | Кто это |
78
+ | --- | --- |
79
+ | `STEAM_0:1:12345` | игрок с этим SteamID |
80
+ | `ip:1.2.3.4` | игрок с этого адреса - для игры без Steam |
81
+ | `name:Bob` | игрок с ником `Bob`, у которого `setinfo _pw` - пароль записи |
82
+
83
+ ## Файл
84
+
85
+ `perms.yaml` (или `perms.json`) в папке конфигов, `addons/amxmodx/configs/` -
86
+ или `configs/<baseDir>/`, если проект задал `baseDir` у config-core, как и для
87
+ остальных конфигов. Его пишут команды и веб-редактор, его можно править руками;
88
+ какой файл прочитан, сервер пишет в консоль при старте:
89
+
90
+ ```yaml
91
+ groups:
92
+ default:
93
+ permissions: [kz.top]
94
+ vip:
95
+ weight: 10
96
+ permissions: [amxx.reservation, shop.discount]
97
+ admin:
98
+ weight: 50
99
+ inherits: [vip]
100
+ permissions: [amxx.kick, amxx.ban, "kz.admin.*", "-kz.admin.wipe"]
101
+ users:
102
+ - id: "STEAM_0:1:12345"
103
+ name: Bob
104
+ groups: [admin]
105
+ permissions: [kz.admin.wipe]
106
+ - id: "STEAM_0:0:777"
107
+ groups:
108
+ - { group: vip, until: 2026-12-01T00:00:00Z }
109
+ ```
110
+
111
+ Право или группа со сроком или для одного сервера записываются объектом:
112
+ `{ node: kz.vip, until: 2026-12-01T00:00:00Z, server: kz1 }`. Время - UTC.
113
+
114
+ > [!WARNING]
115
+ > Ошибку в файле сервер называет в консоли со строкой, а права, прочитанные до
116
+ > неё, остаются: из-за опечатки сервер не останется без админов.
117
+
118
+ ## MySQL
119
+
120
+ Несколько серверов делят один набор прав в MySQL. Адрес базы пишется в
121
+ `addons/amxts/.env` сервера:
122
+
123
+ ```ini
124
+ PERMS_DB=mysql://perms:secret@127.0.0.1:3306/perms
125
+ PERMS_SERVER=kz1
126
+ ```
127
+
128
+ Модуль создаёт свои таблицы (`perms_groups`, `perms_grants`, `perms_users`,
129
+ `perms_members`, `perms_revision`) при первом запуске. Изменение на одном
130
+ сервере доходит до остальных за пять секунд.
131
+
132
+ `PERMS_SERVER` - имя этого сервера: группа или право, выданные с `server: kz1`,
133
+ действуют только на нём.
134
+
135
+ ## В коде
136
+
137
+ ```ts
138
+ perms.has(player, "myplugin.kick"); // true или false
139
+ perms.groupOf(player); // "admin"
140
+ perms.groupsOf(player); // ["admin", "default"]
141
+ perms.addGroup(player, "vip", { for: 30 * 86400 }); // секунды; без for - навсегда
142
+ perms.removeGroup(player, "vip");
143
+ perms.set(player, "myplugin.trail", true, { for: 3600 });
144
+ perms.unset(player, "myplugin.trail");
145
+
146
+ perms.addEventListener("change", ({ player }) => {
147
+ if (perms.has(player, "myplugin.trail")) startTrail(player);
148
+ });
149
+ ```
150
+
151
+ `"change"` приходит, когда права игрока меняются: он зашёл, их изменила команда
152
+ или редактор, или какое-то право закончилось.
153
+
154
+ ## Команды
155
+
156
+ В консоли сервера и через rcon:
157
+
158
+ ```
159
+ perms user <игрок|id> info
160
+ perms user <игрок|id> group add <группа> [30d]
161
+ perms user <игрок|id> group remove <группа>
162
+ perms user <игрок|id> permission set <узел> [true|false] [30d]
163
+ perms user <игрок|id> permission unset <узел>
164
+ perms group <группа> create [вес]
165
+ perms group <группа> delete | info
166
+ perms group <группа> permission set <узел> [true|false] [30d]
167
+ perms group <группа> permission unset <узел>
168
+ perms group <группа> parent add|remove <группа>
169
+ perms group <группа> weight <n>
170
+ perms groups
171
+ perms check <игрок|id> <узел>
172
+ perms nodes [плагин]
173
+ perms editor [close]
174
+ perms reload
175
+ perms import [users.ini]
176
+ ```
177
+
178
+ Игрок - это `#userid`, часть ника или id записи (`STEAM_0:1:12345`). Срок -
179
+ `45s`, `90m`, `12h`, `30d` или `2w`.
180
+
181
+ В игре игрок с правом `perms.view` выполняет команды, которые только показывают, -
182
+ `groups`, `check`, `nodes`, `user ... info` и `group ... info` - в своей
183
+ консоли или в чате (`/perms groups`); ответ приходит в его консоль. Права
184
+ меняются только из консоли сервера и в веб-редакторе.
185
+
186
+ ## Веб-редактор
187
+
188
+ ```
189
+ perms editor
190
+ [perms] Open the editor (an hour): https://amxts.github.io/modules/perms/editor#k3x9q2mw7a
191
+ ```
192
+
193
+ Ссылка открывает права сервера в браузере: узлы каждого плагина с описаниями,
194
+ колонка на каждую группу, группы, игроки. **Сохранить** отправляет изменения
195
+ обратно; сервер применяет их за несколько секунд, без рестарта, а страница
196
+ показывает, что он сделал.
197
+
198
+ - Его открывает консоль сервера; из игры его не открыть.
199
+ - Редактировать может любой, у кого есть ссылка, пока сессия жива: час или до
200
+ `perms editor close`. Храните её как пароль.
201
+ - В редактор уходят группы, права, SteamID и ники. Пароли и адрес базы никогда
202
+ не покидают сервер.
203
+ - Сессия хранится час на сервисе amxts. Чтобы хранить их у себя, запустите
204
+ хранилище из папки `store/` этого пакета (Cloudflare Worker) и укажите его
205
+ адрес в `PERMS_EDITOR_URL` в `.env`. Тогда ссылка будет вашего хранилища, и
206
+ она откроет ту же страницу с вашим хранилищем:
207
+
208
+ ```
209
+ [perms] Open the editor (an hour): https://perms.myserver.com/editor#k3x9q2mw7a
210
+ ```
211
+
212
+ ## Вместо admin.amxx
213
+
214
+ 1. `npx amxts module add perms`, сборка и выкладка.
215
+ 2. Закомментируйте `admin.amxx` (или `admin_sql.amxx`) в `plugins.ini`. Пока
216
+ один из них работает, модуль оставляет флаги игроков ему и пишет об этом в
217
+ консоль.
218
+ 3. В консоли сервера: `perms import users.ini`. Каждый админ становится записью,
219
+ а его флаги - его личными правами `amxx.*`. Проверьте любого командой
220
+ `perms user <ник> info`.
221
+
222
+ `users.ini` больше не читается; он остаётся, чтобы можно было вернуться.
223
+
224
+ Pawn-плагины работают без изменений: модуль ставит каждому игроку флаги AMX Mod X
225
+ из его узлов `amxx.*`, поэтому `get_user_flags`, `access`, `cmd_access`,
226
+ `is_user_admin` и `cmdaccess.ini` видят права каждого игрока.
227
+ Игрок без узлов `amxx.*` получает `amx_default_access` (`z`). Модуль держит и
228
+ `amx_mode`, `amx_password_field`, `amx_default_access` и `amx_reloadadmins`,
229
+ которыми пользуются конфиги сервера и плагины.
230
+
231
+ | Узел | Флаг |
232
+ | --- | --- |
233
+ | `amxx.immunity` | `a` |
234
+ | `amxx.reservation` | `b` |
235
+ | `amxx.kick` | `c` |
236
+ | `amxx.ban` | `d` |
237
+ | `amxx.slay` | `e` |
238
+ | `amxx.map` | `f` |
239
+ | `amxx.cvar` | `g` |
240
+ | `amxx.cfg` | `h` |
241
+ | `amxx.chat` | `i` |
242
+ | `amxx.vote` | `j` |
243
+ | `amxx.password` | `k` |
244
+ | `amxx.rcon` | `l` |
245
+ | `amxx.levelA` ... `amxx.levelH` | `m` ... `t` |
246
+ | `amxx.menu` | `u` |
247
+ | `amxx.banTemp` | `v` |
248
+ | `amxx.admin` | `y` |
249
+
250
+ > [!WARNING]
251
+ > **amx_mode**
252
+ >
253
+ > `amx_mode 2` кикает игрока без записи, как `admin.amxx`: закрытый сервер.
254
+ > `amx_mode 0` никому ничего не даёт.
255
+
256
+ Pawn-плагины могут проверять узлы сами: [PAWN.ru.md](PAWN.ru.md).
257
+
258
+ ## Настройки
259
+
260
+ ```ts
261
+ export default defineConfig({
262
+ modules: ["@amxts/perms"],
263
+ perms: {
264
+ file: "perms", // configs/perms.yaml
265
+ editor: "", // хранилище редактора; PERMS_EDITOR_URL важнее
266
+ },
267
+ });
268
+ ```
269
+
270
+ ## Лицензия
271
+
272
+ MIT