web-mojo 2.2.55 → 2.2.57
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/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +314 -1
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunks/{version-Gcf9opot.js → version-BVADfTA5.js} +2 -2
- package/dist/chunks/{version-Gcf9opot.js.map → version-BVADfTA5.js.map} +1 -1
- package/dist/chunks/{version-ZWtWuDxk.js → version-OyPGnx30.js} +4 -4
- package/dist/chunks/{version-ZWtWuDxk.js.map → version-OyPGnx30.js.map} +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +1 -1
- package/package.json +1 -1
package/dist/admin.es.js
CHANGED
|
@@ -10,7 +10,7 @@ import { M as MapView, a as MetricsCountryMapView } from "./chunks/MetricsCountr
|
|
|
10
10
|
import { M as Model, C as Collection } from "./chunks/Collection-DaiL0uGl.js";
|
|
11
11
|
import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-EJ9cOfPF.js";
|
|
12
12
|
import { W } from "./chunks/WebApp-6qvqmOts.js";
|
|
13
|
-
import { B, a, V, b, c, d } from "./chunks/version-
|
|
13
|
+
import { B, a, V, b, c, d } from "./chunks/version-OyPGnx30.js";
|
|
14
14
|
class AdminHeaderView extends View {
|
|
15
15
|
constructor(options = {}) {
|
|
16
16
|
super({
|
|
@@ -2249,6 +2249,310 @@ class GeoLocatedIPTablePage extends TablePage {
|
|
|
2249
2249
|
}
|
|
2250
2250
|
}
|
|
2251
2251
|
}
|
|
2252
|
+
class ApiKey extends Model {
|
|
2253
|
+
constructor(data = {}, options = {}) {
|
|
2254
|
+
super(data, {
|
|
2255
|
+
endpoint: "/api/group/apikey",
|
|
2256
|
+
...options
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
class ApiKeyList extends Collection {
|
|
2261
|
+
constructor(options = {}) {
|
|
2262
|
+
super({
|
|
2263
|
+
ModelClass: ApiKey,
|
|
2264
|
+
endpoint: "/api/group/apikey",
|
|
2265
|
+
size: 25,
|
|
2266
|
+
...options
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
const ApiKeyForms = {
|
|
2271
|
+
create: {
|
|
2272
|
+
title: "Create API Key",
|
|
2273
|
+
fields: [
|
|
2274
|
+
{
|
|
2275
|
+
name: "name",
|
|
2276
|
+
type: "text",
|
|
2277
|
+
label: "Name",
|
|
2278
|
+
placeholder: "Mobile App v2",
|
|
2279
|
+
required: true,
|
|
2280
|
+
columns: 12,
|
|
2281
|
+
help: "A descriptive name to identify this key."
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "group",
|
|
2285
|
+
type: "number",
|
|
2286
|
+
label: "Group ID",
|
|
2287
|
+
required: true,
|
|
2288
|
+
columns: 6,
|
|
2289
|
+
help: "The group this key is scoped to."
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
name: "permissions",
|
|
2293
|
+
type: "textarea",
|
|
2294
|
+
label: "Permissions (JSON)",
|
|
2295
|
+
placeholder: '{"view_orders": true, "create_orders": true}',
|
|
2296
|
+
columns: 12,
|
|
2297
|
+
help: "JSON dict of permissions to grant. Leave empty for no permissions."
|
|
2298
|
+
}
|
|
2299
|
+
]
|
|
2300
|
+
},
|
|
2301
|
+
edit: {
|
|
2302
|
+
title: "Edit API Key",
|
|
2303
|
+
fields: [
|
|
2304
|
+
{
|
|
2305
|
+
name: "name",
|
|
2306
|
+
type: "text",
|
|
2307
|
+
label: "Name",
|
|
2308
|
+
required: true,
|
|
2309
|
+
columns: 12
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
name: "is_active",
|
|
2313
|
+
type: "switch",
|
|
2314
|
+
label: "Active",
|
|
2315
|
+
columns: 12,
|
|
2316
|
+
help: "Deactivate to revoke access without deleting the key."
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
name: "permissions",
|
|
2320
|
+
type: "textarea",
|
|
2321
|
+
label: "Permissions (JSON)",
|
|
2322
|
+
columns: 12,
|
|
2323
|
+
help: "JSON dict of granted permissions."
|
|
2324
|
+
}
|
|
2325
|
+
]
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
class ApiKeyView extends View {
|
|
2329
|
+
constructor(options = {}) {
|
|
2330
|
+
super({
|
|
2331
|
+
className: "api-key-view",
|
|
2332
|
+
...options
|
|
2333
|
+
});
|
|
2334
|
+
this.model = options.model || new ApiKey(options.data || {});
|
|
2335
|
+
this.template = `
|
|
2336
|
+
<div class="api-key-view-container">
|
|
2337
|
+
<!-- Header -->
|
|
2338
|
+
<div class="d-flex justify-content-between align-items-start mb-4">
|
|
2339
|
+
<!-- Left: Icon & Identity -->
|
|
2340
|
+
<div class="d-flex align-items-center gap-3">
|
|
2341
|
+
<div class="fs-1 text-primary">
|
|
2342
|
+
<i class="bi bi-key"></i>
|
|
2343
|
+
</div>
|
|
2344
|
+
<div>
|
|
2345
|
+
<h3 class="mb-1">{{model.name|default('Unnamed Key')}}</h3>
|
|
2346
|
+
<div class="text-muted small">
|
|
2347
|
+
ID: {{model.id}}
|
|
2348
|
+
<span class="mx-2">|</span>
|
|
2349
|
+
Group: {{model.group.name|default(model.group)}}
|
|
2350
|
+
</div>
|
|
2351
|
+
<div class="mt-1">
|
|
2352
|
+
<span class="badge {{model.is_active|boolean('bg-success','bg-secondary')}}">
|
|
2353
|
+
{{model.is_active|boolean('Active','Inactive')}}
|
|
2354
|
+
</span>
|
|
2355
|
+
</div>
|
|
2356
|
+
</div>
|
|
2357
|
+
</div>
|
|
2358
|
+
|
|
2359
|
+
<!-- Right: Meta & Actions -->
|
|
2360
|
+
<div class="d-flex align-items-start gap-4">
|
|
2361
|
+
<div class="text-end">
|
|
2362
|
+
<div class="text-muted small">Created</div>
|
|
2363
|
+
<div>{{model.created|datetime}}</div>
|
|
2364
|
+
</div>
|
|
2365
|
+
<div data-container="apikey-context-menu"></div>
|
|
2366
|
+
</div>
|
|
2367
|
+
</div>
|
|
2368
|
+
|
|
2369
|
+
<!-- Details -->
|
|
2370
|
+
<div class="list-group mb-3">
|
|
2371
|
+
<div class="list-group-item">
|
|
2372
|
+
<h6 class="mb-1 text-muted">Token Preview</h6>
|
|
2373
|
+
<p class="mb-1 font-monospace small text-muted">
|
|
2374
|
+
The raw token is only shown once at creation time.
|
|
2375
|
+
</p>
|
|
2376
|
+
</div>
|
|
2377
|
+
<div class="list-group-item">
|
|
2378
|
+
<h6 class="mb-1 text-muted">Permissions</h6>
|
|
2379
|
+
{{#model.permissions}}
|
|
2380
|
+
<pre class="mb-0 small">{{model.permissions|json}}</pre>
|
|
2381
|
+
{{/model.permissions}}
|
|
2382
|
+
{{^model.permissions}}
|
|
2383
|
+
<span class="text-muted small">No permissions granted</span>
|
|
2384
|
+
{{/model.permissions}}
|
|
2385
|
+
</div>
|
|
2386
|
+
{{#model.limits}}
|
|
2387
|
+
<div class="list-group-item">
|
|
2388
|
+
<h6 class="mb-1 text-muted">Rate Limit Overrides</h6>
|
|
2389
|
+
<pre class="mb-0 small">{{model.limits|json}}</pre>
|
|
2390
|
+
</div>
|
|
2391
|
+
{{/model.limits}}
|
|
2392
|
+
<div class="list-group-item">
|
|
2393
|
+
<h6 class="mb-1 text-muted">Usage</h6>
|
|
2394
|
+
<p class="mb-0 small text-muted">
|
|
2395
|
+
Include in requests as:
|
|
2396
|
+
<code>Authorization: apikey <token></code>
|
|
2397
|
+
</p>
|
|
2398
|
+
</div>
|
|
2399
|
+
</div>
|
|
2400
|
+
</div>
|
|
2401
|
+
`;
|
|
2402
|
+
}
|
|
2403
|
+
async onInit() {
|
|
2404
|
+
const isActive = this.model.get("is_active");
|
|
2405
|
+
const apiKeyMenu = new ContextMenu({
|
|
2406
|
+
containerId: "apikey-context-menu",
|
|
2407
|
+
className: "context-menu-view header-menu-absolute",
|
|
2408
|
+
context: this.model,
|
|
2409
|
+
config: {
|
|
2410
|
+
icon: "bi-three-dots-vertical",
|
|
2411
|
+
items: [
|
|
2412
|
+
{ label: "Edit", action: "edit-key", icon: "bi-pencil" },
|
|
2413
|
+
isActive ? { label: "Deactivate", action: "deactivate-key", icon: "bi-x-circle" } : { label: "Activate", action: "activate-key", icon: "bi-check-circle" },
|
|
2414
|
+
{ type: "divider" },
|
|
2415
|
+
{ label: "Delete Key", action: "delete-key", icon: "bi-trash", danger: true }
|
|
2416
|
+
]
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
this.addChild(apiKeyMenu);
|
|
2420
|
+
}
|
|
2421
|
+
async onActionEditKey() {
|
|
2422
|
+
const app = this.getApp();
|
|
2423
|
+
const resp = await app.showModelForm({
|
|
2424
|
+
title: `Edit API Key — ${this.model.get("name")}`,
|
|
2425
|
+
model: this.model,
|
|
2426
|
+
formConfig: ApiKeyForms.edit
|
|
2427
|
+
});
|
|
2428
|
+
if (resp) {
|
|
2429
|
+
this.render();
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
async onActionDeactivateKey() {
|
|
2433
|
+
const app = this.getApp();
|
|
2434
|
+
const confirmed = await app.confirm({
|
|
2435
|
+
title: "Deactivate API Key",
|
|
2436
|
+
message: `Deactivate "${this.model.get("name")}"? Requests using this key will be rejected.`,
|
|
2437
|
+
confirmLabel: "Deactivate",
|
|
2438
|
+
confirmClass: "btn-warning"
|
|
2439
|
+
});
|
|
2440
|
+
if (!confirmed) return;
|
|
2441
|
+
app.showLoading();
|
|
2442
|
+
const resp = await this.model.save({ is_active: false });
|
|
2443
|
+
app.hideLoading();
|
|
2444
|
+
if (resp && resp.success !== false) {
|
|
2445
|
+
app.toast.success("API key deactivated");
|
|
2446
|
+
this.render();
|
|
2447
|
+
} else {
|
|
2448
|
+
app.toast.error("Failed to deactivate key");
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
async onActionActivateKey() {
|
|
2452
|
+
const app = this.getApp();
|
|
2453
|
+
app.showLoading();
|
|
2454
|
+
const resp = await this.model.save({ is_active: true });
|
|
2455
|
+
app.hideLoading();
|
|
2456
|
+
if (resp && resp.success !== false) {
|
|
2457
|
+
app.toast.success("API key activated");
|
|
2458
|
+
this.render();
|
|
2459
|
+
} else {
|
|
2460
|
+
app.toast.error("Failed to activate key");
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
async onActionDeleteKey() {
|
|
2464
|
+
const app = this.getApp();
|
|
2465
|
+
const confirmed = await app.confirm({
|
|
2466
|
+
title: "Delete API Key",
|
|
2467
|
+
message: `Permanently delete "${this.model.get("name")}"? This cannot be undone.`,
|
|
2468
|
+
confirmLabel: "Delete",
|
|
2469
|
+
confirmClass: "btn-danger"
|
|
2470
|
+
});
|
|
2471
|
+
if (!confirmed) return;
|
|
2472
|
+
app.showLoading();
|
|
2473
|
+
const resp = await this.model.delete();
|
|
2474
|
+
app.hideLoading();
|
|
2475
|
+
if (resp && resp.success !== false) {
|
|
2476
|
+
app.toast.success("API key deleted");
|
|
2477
|
+
this.emit("deleted", { model: this.model });
|
|
2478
|
+
} else {
|
|
2479
|
+
app.toast.error("Failed to delete key");
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
ApiKey.VIEW_CLASS = ApiKeyView;
|
|
2484
|
+
ApiKey.ADD_FORM = ApiKeyForms.create;
|
|
2485
|
+
ApiKey.EDIT_FORM = ApiKeyForms.edit;
|
|
2486
|
+
class ApiKeyTablePage extends TablePage {
|
|
2487
|
+
constructor(options = {}) {
|
|
2488
|
+
super({
|
|
2489
|
+
...options,
|
|
2490
|
+
name: "admin_api_keys",
|
|
2491
|
+
pageName: "API Keys",
|
|
2492
|
+
router: "admin/api-keys",
|
|
2493
|
+
Collection: ApiKeyList,
|
|
2494
|
+
itemViewClass: ApiKeyView,
|
|
2495
|
+
viewDialogOptions: {
|
|
2496
|
+
header: false,
|
|
2497
|
+
size: "lg"
|
|
2498
|
+
},
|
|
2499
|
+
columns: [
|
|
2500
|
+
{ key: "id", label: "ID", width: "70px", sortable: true, class: "text-muted" },
|
|
2501
|
+
{ key: "name", label: "Name", sortable: true },
|
|
2502
|
+
{ key: "group.name", label: "Group", sortable: true, formatter: "default('—')" },
|
|
2503
|
+
{
|
|
2504
|
+
key: "is_active",
|
|
2505
|
+
label: "Status",
|
|
2506
|
+
formatter: "boolean('Active|bg-success','Inactive|bg-secondary')|badge",
|
|
2507
|
+
width: "100px"
|
|
2508
|
+
},
|
|
2509
|
+
{ key: "created", label: "Created", formatter: "datetime", sortable: true }
|
|
2510
|
+
],
|
|
2511
|
+
selectable: true,
|
|
2512
|
+
searchable: true,
|
|
2513
|
+
sortable: true,
|
|
2514
|
+
filterable: true,
|
|
2515
|
+
paginated: true,
|
|
2516
|
+
showRefresh: true,
|
|
2517
|
+
showAdd: true,
|
|
2518
|
+
showExport: false,
|
|
2519
|
+
addButtonLabel: "New API Key",
|
|
2520
|
+
emptyMessage: "No API keys found.",
|
|
2521
|
+
tableOptions: {
|
|
2522
|
+
striped: true,
|
|
2523
|
+
bordered: false,
|
|
2524
|
+
hover: true,
|
|
2525
|
+
responsive: false
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
// Override to intercept and show the one-time token after model.save()
|
|
2530
|
+
async onActionAdd() {
|
|
2531
|
+
const app = this.getApp();
|
|
2532
|
+
const model = new ApiKey();
|
|
2533
|
+
const result = await app.showForm({
|
|
2534
|
+
model,
|
|
2535
|
+
...ApiKeyForms.create
|
|
2536
|
+
});
|
|
2537
|
+
if (!result) return;
|
|
2538
|
+
const resp = await model.save(result);
|
|
2539
|
+
if (!resp?.data?.status) {
|
|
2540
|
+
app.showError(resp?.data?.error || "Failed to create API key");
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
const token = resp.data?.data?.token;
|
|
2544
|
+
await app.showAlert({
|
|
2545
|
+
title: "API Key Created — Save Your Token",
|
|
2546
|
+
message: token ? `Copy this token now. It will not be shown again.
|
|
2547
|
+
|
|
2548
|
+
${token}` : "API key created successfully.",
|
|
2549
|
+
type: token ? "warning" : "success",
|
|
2550
|
+
size: "lg"
|
|
2551
|
+
});
|
|
2552
|
+
this.collection.add(model);
|
|
2553
|
+
this.tableView?.refresh();
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2252
2556
|
class IncidentDashboardHeader extends View {
|
|
2253
2557
|
constructor(options = {}) {
|
|
2254
2558
|
super({
|
|
@@ -9527,6 +9831,7 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9527
9831
|
app.registerPage("system/push/devices", PushDeviceTablePage, { permissions: ["manage_users"] });
|
|
9528
9832
|
app.registerPage("system/phonehub/numbers", PhoneNumberTablePage, { permissions: ["manage_users"] });
|
|
9529
9833
|
app.registerPage("system/phonehub/sms", SMSTablePage, { permissions: ["manage_users"] });
|
|
9834
|
+
app.registerPage("system/api-keys", ApiKeyTablePage, { permissions: ["manage_groups", "manage_group"] });
|
|
9530
9835
|
if (addToMenu && app.sidebar && app.sidebar.getMenuConfig) {
|
|
9531
9836
|
const adminMenuConfig = app.sidebar.getMenuConfig("system");
|
|
9532
9837
|
if (adminMenuConfig && adminMenuConfig.items) {
|
|
@@ -9628,6 +9933,12 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9628
9933
|
route: "?page=system/metrics/permissions",
|
|
9629
9934
|
icon: "bi-bar-chart-line",
|
|
9630
9935
|
permissions: ["manage_metrics"]
|
|
9936
|
+
},
|
|
9937
|
+
{
|
|
9938
|
+
text: "API Keys",
|
|
9939
|
+
route: "?page=system/api-keys",
|
|
9940
|
+
icon: "bi-key",
|
|
9941
|
+
permissions: ["manage_groups", "manage_group"]
|
|
9631
9942
|
}
|
|
9632
9943
|
]
|
|
9633
9944
|
},
|
|
@@ -9729,6 +10040,8 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9729
10040
|
}
|
|
9730
10041
|
export {
|
|
9731
10042
|
AdminDashboardPage,
|
|
10043
|
+
ApiKeyTablePage,
|
|
10044
|
+
ApiKeyView,
|
|
9732
10045
|
B as BUILD_TIME,
|
|
9733
10046
|
DeviceView,
|
|
9734
10047
|
EmailDomainTablePage,
|