tempest-express-sdk 0.29.0 → 0.30.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/README.md +2 -2
- package/dist/{chunk-7T4MZJZT.js → chunk-DK46733U.js} +3 -3
- package/dist/{chunk-7T4MZJZT.js.map → chunk-DK46733U.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +477 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +268 -4
- package/dist/index.d.ts +268 -4
- package/dist/index.js +474 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z, looseBoolean, toDict, PasswordUtils } from './chunk-
|
|
2
|
-
export { PasswordUtils, VERSION, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, centsField, corsSettingsShape, databaseSettingsShape, looseBoolean as envBoolean, hexColorField, latitudeField, loadSettings, longitudeField, looseBoolean, nonEmptyStrField, nonNegativeFloatField, nonNegativeIntField, percentField, portField, positiveFloatField, positiveIntField, priceField, ratingField, ratioField, serverSettingsShape, slugField, toDict, z } from './chunk-
|
|
1
|
+
import { z, looseBoolean, toDict, PasswordUtils } from './chunk-DK46733U.js';
|
|
2
|
+
export { PasswordUtils, VERSION, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, centsField, corsSettingsShape, databaseSettingsShape, looseBoolean as envBoolean, hexColorField, latitudeField, loadSettings, longitudeField, looseBoolean, nonEmptyStrField, nonNegativeFloatField, nonNegativeIntField, percentField, portField, positiveFloatField, positiveIntField, priceField, ratingField, ratioField, serverSettingsShape, slugField, toDict, z } from './chunk-DK46733U.js';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
4
|
import { Model, column, sql, BaseRepository, RecordNotFound, detectDialect, columnsOf, NodeSqliteDriver, AsyncEngine, or, and } from 'tempest-db-js';
|
|
5
5
|
export { AsyncEngine, AsyncResult, AsyncSession, BaseRepository, Column, DeleteBuilder, InsertBuilder, Model, NoResultError, NodeSqliteDriver, PostgresDialect, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncSession, UpdateBuilder, and, belongsTo, column, columnsOf, createEngine, createSyncEngine, del, detectDialect, getDialect, hasMany, insert, join, loadRelations, not, or, parseDatabaseUrl, select, sql, update } from 'tempest-db-js';
|
|
@@ -8546,6 +8546,7 @@ var TaskManager = class {
|
|
|
8546
8546
|
broker;
|
|
8547
8547
|
queue;
|
|
8548
8548
|
handlers = /* @__PURE__ */ new Map();
|
|
8549
|
+
metadata = /* @__PURE__ */ new Map();
|
|
8549
8550
|
unsubscribe = null;
|
|
8550
8551
|
/**
|
|
8551
8552
|
* @param options - Broker and queue name.
|
|
@@ -8559,9 +8560,32 @@ var TaskManager = class {
|
|
|
8559
8560
|
*
|
|
8560
8561
|
* @param name - The task name.
|
|
8561
8562
|
* @param handler - The handler invoked with the task payload.
|
|
8563
|
+
* @param options - Description and declared schedule, surfaced by
|
|
8564
|
+
* {@link TaskManager.inventory} and by the admin panel's tasks page.
|
|
8562
8565
|
*/
|
|
8563
|
-
register(name, handler) {
|
|
8566
|
+
register(name, handler, options = {}) {
|
|
8564
8567
|
this.handlers.set(name, handler);
|
|
8568
|
+
this.metadata.set(name, options);
|
|
8569
|
+
}
|
|
8570
|
+
/**
|
|
8571
|
+
* Return what this process would run, ordered by name.
|
|
8572
|
+
*
|
|
8573
|
+
* This is the **declared** side of background work — the handlers this
|
|
8574
|
+
* process knows about — not queue state. A broker's pending depth is not
|
|
8575
|
+
* something the manager can see, and a screen that implied otherwise would
|
|
8576
|
+
* be worse than one that says nothing.
|
|
8577
|
+
*
|
|
8578
|
+
* @returns One entry per registered task.
|
|
8579
|
+
*/
|
|
8580
|
+
inventory() {
|
|
8581
|
+
return [...this.handlers.keys()].sort((left, right) => left.localeCompare(right)).map((name) => {
|
|
8582
|
+
const meta = this.metadata.get(name) ?? {};
|
|
8583
|
+
return {
|
|
8584
|
+
name,
|
|
8585
|
+
description: meta.description ?? null,
|
|
8586
|
+
schedule: meta.schedule ?? null
|
|
8587
|
+
};
|
|
8588
|
+
});
|
|
8565
8589
|
}
|
|
8566
8590
|
/**
|
|
8567
8591
|
* Enqueue a task by name.
|
|
@@ -8595,6 +8619,180 @@ var TaskManager = class {
|
|
|
8595
8619
|
this.unsubscribe = null;
|
|
8596
8620
|
}
|
|
8597
8621
|
};
|
|
8622
|
+
var JobStatus = {
|
|
8623
|
+
/** Written, not started. */
|
|
8624
|
+
QUEUED: "queued",
|
|
8625
|
+
/** A worker picked it up. */
|
|
8626
|
+
RUNNING: "running",
|
|
8627
|
+
/** Finished cleanly. */
|
|
8628
|
+
SUCCEEDED: "succeeded",
|
|
8629
|
+
/** Finished with an error. */
|
|
8630
|
+
FAILED: "failed",
|
|
8631
|
+
/** An operator stopped it before it finished. */
|
|
8632
|
+
CANCELLED: "cancelled"
|
|
8633
|
+
};
|
|
8634
|
+
var TERMINAL = [
|
|
8635
|
+
JobStatus.SUCCEEDED,
|
|
8636
|
+
JobStatus.FAILED,
|
|
8637
|
+
JobStatus.CANCELLED
|
|
8638
|
+
];
|
|
8639
|
+
var BaseJobModel = class extends BaseModel {
|
|
8640
|
+
/** The task name this run belongs to. */
|
|
8641
|
+
name = column.varchar(128).notNull();
|
|
8642
|
+
/** Lifecycle state — a {@link JobStatus} value. */
|
|
8643
|
+
status = column.varchar(16).notNull().default(JobStatus.QUEUED);
|
|
8644
|
+
/** The payload the run was started with. */
|
|
8645
|
+
payload = column.json();
|
|
8646
|
+
/** Whatever the run produced, for an operator to read afterwards. */
|
|
8647
|
+
result = column.json();
|
|
8648
|
+
/** The failure message, when the run failed. */
|
|
8649
|
+
error = column.text();
|
|
8650
|
+
/** How many times the run has been attempted. */
|
|
8651
|
+
attempts = column.integer().notNull().default(0);
|
|
8652
|
+
/** When a worker picked it up. */
|
|
8653
|
+
startedAt = column.datetime();
|
|
8654
|
+
/** When it reached a terminal state. */
|
|
8655
|
+
finishedAt = column.datetime();
|
|
8656
|
+
};
|
|
8657
|
+
var JobStore = class {
|
|
8658
|
+
/**
|
|
8659
|
+
* @param model - The concrete {@link BaseJobModel} subclass.
|
|
8660
|
+
* @param session - The session rows are written on.
|
|
8661
|
+
*/
|
|
8662
|
+
constructor(model, session) {
|
|
8663
|
+
this.model = model;
|
|
8664
|
+
this.repository = new BaseRepository(model, session);
|
|
8665
|
+
}
|
|
8666
|
+
model;
|
|
8667
|
+
repository;
|
|
8668
|
+
/**
|
|
8669
|
+
* Record a job about to run.
|
|
8670
|
+
*
|
|
8671
|
+
* @param name - The task name.
|
|
8672
|
+
* @param payload - The payload the run was started with.
|
|
8673
|
+
* @returns The created row.
|
|
8674
|
+
*/
|
|
8675
|
+
async enqueue(name, payload = {}) {
|
|
8676
|
+
return await this.repository.create({
|
|
8677
|
+
name,
|
|
8678
|
+
status: JobStatus.QUEUED,
|
|
8679
|
+
payload,
|
|
8680
|
+
result: null,
|
|
8681
|
+
error: null,
|
|
8682
|
+
attempts: 0,
|
|
8683
|
+
startedAt: null,
|
|
8684
|
+
finishedAt: null
|
|
8685
|
+
});
|
|
8686
|
+
}
|
|
8687
|
+
/**
|
|
8688
|
+
* Mark a job as picked up, counting the attempt.
|
|
8689
|
+
*
|
|
8690
|
+
* @param id - The job id.
|
|
8691
|
+
* @param attempt - Which attempt this is. Default `1`.
|
|
8692
|
+
* @returns How many rows changed.
|
|
8693
|
+
*/
|
|
8694
|
+
async start(id, attempt = 1) {
|
|
8695
|
+
return await this.repository.update(
|
|
8696
|
+
{ id },
|
|
8697
|
+
{
|
|
8698
|
+
status: JobStatus.RUNNING,
|
|
8699
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
8700
|
+
attempts: attempt
|
|
8701
|
+
}
|
|
8702
|
+
);
|
|
8703
|
+
}
|
|
8704
|
+
/**
|
|
8705
|
+
* Mark a job as finished cleanly.
|
|
8706
|
+
*
|
|
8707
|
+
* @param id - The job id.
|
|
8708
|
+
* @param result - Whatever the run produced.
|
|
8709
|
+
* @returns How many rows changed.
|
|
8710
|
+
*/
|
|
8711
|
+
async succeed(id, result = {}) {
|
|
8712
|
+
return await this.repository.update(
|
|
8713
|
+
{ id },
|
|
8714
|
+
{
|
|
8715
|
+
status: JobStatus.SUCCEEDED,
|
|
8716
|
+
result,
|
|
8717
|
+
error: null,
|
|
8718
|
+
finishedAt: /* @__PURE__ */ new Date()
|
|
8719
|
+
}
|
|
8720
|
+
);
|
|
8721
|
+
}
|
|
8722
|
+
/**
|
|
8723
|
+
* Mark a job as failed.
|
|
8724
|
+
*
|
|
8725
|
+
* @param id - The job id.
|
|
8726
|
+
* @param error - The failure, as an `Error` or a message.
|
|
8727
|
+
* @returns How many rows changed.
|
|
8728
|
+
*/
|
|
8729
|
+
async fail(id, error) {
|
|
8730
|
+
return await this.repository.update(
|
|
8731
|
+
{ id },
|
|
8732
|
+
{
|
|
8733
|
+
status: JobStatus.FAILED,
|
|
8734
|
+
error: error instanceof Error ? error.message : String(error),
|
|
8735
|
+
finishedAt: /* @__PURE__ */ new Date()
|
|
8736
|
+
}
|
|
8737
|
+
);
|
|
8738
|
+
}
|
|
8739
|
+
/**
|
|
8740
|
+
* Ask a job to stop.
|
|
8741
|
+
*
|
|
8742
|
+
* A job already in a terminal state is left alone and reported as not
|
|
8743
|
+
* cancelled, so an operator clicking cancel on a run that just finished sees
|
|
8744
|
+
* the truth rather than a row rewritten under them.
|
|
8745
|
+
*
|
|
8746
|
+
* @param id - The job id.
|
|
8747
|
+
* @returns Whether the row moved to `cancelled`.
|
|
8748
|
+
*/
|
|
8749
|
+
async cancel(id) {
|
|
8750
|
+
const row = await this.repository.getByIdOrNull(id);
|
|
8751
|
+
if (row === null || TERMINAL.includes(String(row.status))) return false;
|
|
8752
|
+
const changed = await this.repository.update(
|
|
8753
|
+
{ id },
|
|
8754
|
+
{
|
|
8755
|
+
status: JobStatus.CANCELLED,
|
|
8756
|
+
finishedAt: /* @__PURE__ */ new Date()
|
|
8757
|
+
}
|
|
8758
|
+
);
|
|
8759
|
+
return changed > 0;
|
|
8760
|
+
}
|
|
8761
|
+
/**
|
|
8762
|
+
* Read one job row.
|
|
8763
|
+
*
|
|
8764
|
+
* @param id - The job id.
|
|
8765
|
+
* @returns The row, or `null`.
|
|
8766
|
+
*/
|
|
8767
|
+
async get(id) {
|
|
8768
|
+
return await this.repository.getByIdOrNull(id);
|
|
8769
|
+
}
|
|
8770
|
+
/**
|
|
8771
|
+
* Read a page of jobs, newest first.
|
|
8772
|
+
*
|
|
8773
|
+
* @param filter - Page, size and optional `name` / `status` filters.
|
|
8774
|
+
* @returns The page plus its metadata.
|
|
8775
|
+
*/
|
|
8776
|
+
async list(filter = {}) {
|
|
8777
|
+
const filters = {};
|
|
8778
|
+
if (filter.name !== void 0 && filter.name !== "") filters.name = filter.name;
|
|
8779
|
+
if (filter.status !== void 0) filters.status = filter.status;
|
|
8780
|
+
const result = await this.repository.paginate({
|
|
8781
|
+
page: filter.page ?? 1,
|
|
8782
|
+
pageSize: filter.pageSize ?? 25,
|
|
8783
|
+
orderBy: "createdAt",
|
|
8784
|
+
ascending: false,
|
|
8785
|
+
...Object.keys(filters).length === 0 ? {} : { filters }
|
|
8786
|
+
});
|
|
8787
|
+
return {
|
|
8788
|
+
items: result.items,
|
|
8789
|
+
total: result.total,
|
|
8790
|
+
page: result.page,
|
|
8791
|
+
pageSize: result.pageSize,
|
|
8792
|
+
pages: result.pages
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8795
|
+
};
|
|
8598
8796
|
|
|
8599
8797
|
// src/flags/backends.ts
|
|
8600
8798
|
function coerceFlag(value) {
|
|
@@ -9667,8 +9865,8 @@ function humanizeField(name) {
|
|
|
9667
9865
|
function adminColumns(model) {
|
|
9668
9866
|
return columnsOf(model);
|
|
9669
9867
|
}
|
|
9670
|
-
function widgetForColumn(
|
|
9671
|
-
const { kind, meta } =
|
|
9868
|
+
function widgetForColumn(column7) {
|
|
9869
|
+
const { kind, meta } = column7.type;
|
|
9672
9870
|
const plain = (widget) => ({
|
|
9673
9871
|
widget,
|
|
9674
9872
|
step: null,
|
|
@@ -9712,11 +9910,11 @@ function widgetForColumn(column6) {
|
|
|
9712
9910
|
return plain("text");
|
|
9713
9911
|
}
|
|
9714
9912
|
}
|
|
9715
|
-
function isColumnOptional(
|
|
9716
|
-
return !
|
|
9913
|
+
function isColumnOptional(column7) {
|
|
9914
|
+
return !column7.flags.notNull || column7.flags.hasDefault || column7.flags.primaryKey;
|
|
9717
9915
|
}
|
|
9718
|
-
function filterForColumn(
|
|
9719
|
-
const { kind, meta } =
|
|
9916
|
+
function filterForColumn(column7) {
|
|
9917
|
+
const { kind, meta } = column7.type;
|
|
9720
9918
|
if (kind === "boolean") {
|
|
9721
9919
|
return {
|
|
9722
9920
|
kind: "select",
|
|
@@ -9740,11 +9938,11 @@ function filterForColumn(column6) {
|
|
|
9740
9938
|
}
|
|
9741
9939
|
return { kind: "text", options: [] };
|
|
9742
9940
|
}
|
|
9743
|
-
function foreignKeyTable(
|
|
9744
|
-
return
|
|
9941
|
+
function foreignKeyTable(column7) {
|
|
9942
|
+
return column7.reference?.table ?? null;
|
|
9745
9943
|
}
|
|
9746
|
-
function isSearchableColumn(
|
|
9747
|
-
const { kind } =
|
|
9944
|
+
function isSearchableColumn(column7) {
|
|
9945
|
+
const { kind } = column7.type;
|
|
9748
9946
|
return kind === "varchar" || kind === "text" || kind === "char";
|
|
9749
9947
|
}
|
|
9750
9948
|
|
|
@@ -10026,8 +10224,8 @@ function formatFieldValue(widget, value) {
|
|
|
10026
10224
|
}
|
|
10027
10225
|
return String(value);
|
|
10028
10226
|
}
|
|
10029
|
-
function literalDefault(
|
|
10030
|
-
const fallback =
|
|
10227
|
+
function literalDefault(column7) {
|
|
10228
|
+
const fallback = column7.defaultValue;
|
|
10031
10229
|
if (fallback === null || fallback.kind !== "literal") return void 0;
|
|
10032
10230
|
return fallback.value;
|
|
10033
10231
|
}
|
|
@@ -10040,19 +10238,19 @@ function buildFormFields(admin, options = {}) {
|
|
|
10040
10238
|
const autocompleteLabels = options.autocompleteLabels ?? {};
|
|
10041
10239
|
const uploads = new Set(admin.uploadFields);
|
|
10042
10240
|
return admin.editableFieldNames().flatMap((name) => {
|
|
10043
|
-
const
|
|
10044
|
-
if (
|
|
10241
|
+
const column7 = columns[name];
|
|
10242
|
+
if (column7 === void 0) return [];
|
|
10045
10243
|
const related = foreignKeys[name];
|
|
10046
10244
|
const autocompleteUrl = autocompleteUrls[name];
|
|
10047
|
-
const spec = uploads.has(name) ? { widget: "file", step: null, options: [] } : autocompleteUrl !== void 0 ? { widget: "autocomplete", step: null, options: [] } : related === void 0 ? widgetForColumn(
|
|
10048
|
-
const raw = name in values ? values[name] : literalDefault(
|
|
10245
|
+
const spec = uploads.has(name) ? { widget: "file", step: null, options: [] } : autocompleteUrl !== void 0 ? { widget: "autocomplete", step: null, options: [] } : related === void 0 ? widgetForColumn(column7) : { widget: "select", step: null, options: related };
|
|
10246
|
+
const raw = name in values ? values[name] : literalDefault(column7);
|
|
10049
10247
|
return [
|
|
10050
10248
|
{
|
|
10051
10249
|
name,
|
|
10052
10250
|
label: humanizeField(name),
|
|
10053
10251
|
widget: spec.widget,
|
|
10054
10252
|
value: spec.widget === "checkbox" ? "" : formatFieldValue(spec.widget, raw),
|
|
10055
|
-
required: !isColumnOptional(
|
|
10253
|
+
required: !isColumnOptional(column7),
|
|
10056
10254
|
checked: spec.widget === "checkbox" && toBoolean(raw),
|
|
10057
10255
|
step: spec.step,
|
|
10058
10256
|
options: spec.options,
|
|
@@ -10069,8 +10267,8 @@ function toBoolean(value) {
|
|
|
10069
10267
|
if (typeof value !== "string") return false;
|
|
10070
10268
|
return ["true", "on", "yes", "1"].includes(value.trim().toLowerCase());
|
|
10071
10269
|
}
|
|
10072
|
-
function coerceValue(
|
|
10073
|
-
const { kind, meta } =
|
|
10270
|
+
function coerceValue(column7, widget, raw) {
|
|
10271
|
+
const { kind, meta } = column7.type;
|
|
10074
10272
|
switch (widget) {
|
|
10075
10273
|
case "number": {
|
|
10076
10274
|
const parsed = Number(raw);
|
|
@@ -10114,10 +10312,10 @@ function parseFormBody(admin, body, options = {}) {
|
|
|
10114
10312
|
const only = options.only === void 0 ? null : new Set(options.only);
|
|
10115
10313
|
for (const name of admin.editableFieldNames()) {
|
|
10116
10314
|
if (only !== null && !only.has(name)) continue;
|
|
10117
|
-
const
|
|
10118
|
-
if (
|
|
10315
|
+
const column7 = columns[name];
|
|
10316
|
+
if (column7 === void 0) continue;
|
|
10119
10317
|
if (uploads.has(name)) continue;
|
|
10120
|
-
const { widget } = widgetForColumn(
|
|
10318
|
+
const { widget } = widgetForColumn(column7);
|
|
10121
10319
|
if (widget === "checkbox") {
|
|
10122
10320
|
data[name] = toBoolean(body[name]);
|
|
10123
10321
|
continue;
|
|
@@ -10125,16 +10323,16 @@ function parseFormBody(admin, body, options = {}) {
|
|
|
10125
10323
|
const submitted = body[name];
|
|
10126
10324
|
const raw = typeof submitted === "string" ? submitted.trim() : "";
|
|
10127
10325
|
if (raw === "") {
|
|
10128
|
-
if (!isColumnOptional(
|
|
10326
|
+
if (!isColumnOptional(column7)) {
|
|
10129
10327
|
errors[name] = "This field is required.";
|
|
10130
10328
|
continue;
|
|
10131
10329
|
}
|
|
10132
|
-
if (
|
|
10330
|
+
if (column7.flags.hasDefault && !(name in body)) continue;
|
|
10133
10331
|
data[name] = null;
|
|
10134
10332
|
continue;
|
|
10135
10333
|
}
|
|
10136
10334
|
try {
|
|
10137
|
-
data[name] = coerceValue(
|
|
10335
|
+
data[name] = coerceValue(column7, widget, raw);
|
|
10138
10336
|
} catch (error) {
|
|
10139
10337
|
errors[name] = error instanceof Error ? error.message : "Invalid value.";
|
|
10140
10338
|
}
|
|
@@ -10152,9 +10350,9 @@ function foreignKeyFields(admin) {
|
|
|
10152
10350
|
const columns = adminColumns(admin.model);
|
|
10153
10351
|
const out = {};
|
|
10154
10352
|
for (const name of admin.editableFieldNames()) {
|
|
10155
|
-
const
|
|
10156
|
-
if (
|
|
10157
|
-
const table = foreignKeyTable(
|
|
10353
|
+
const column7 = columns[name];
|
|
10354
|
+
if (column7 === void 0) continue;
|
|
10355
|
+
const table = foreignKeyTable(column7);
|
|
10158
10356
|
if (table !== null) out[name] = table;
|
|
10159
10357
|
}
|
|
10160
10358
|
return out;
|
|
@@ -10540,7 +10738,14 @@ var AdminSite = class {
|
|
|
10540
10738
|
};
|
|
10541
10739
|
|
|
10542
10740
|
// src/admin/styles.ts
|
|
10543
|
-
var
|
|
10741
|
+
var ADMIN_CSS_EXTRA = `
|
|
10742
|
+
.tempest-log-badge--succeeded { background: #dcfce7; color: #166534; }
|
|
10743
|
+
.tempest-log-badge--failed { background: #fee2e2; color: #991b1b; }
|
|
10744
|
+
.tempest-log-badge--running { background: #dbeafe; color: #1e40af; }
|
|
10745
|
+
.tempest-log-badge--queued { background: #f1f5f9; color: #475569; }
|
|
10746
|
+
.tempest-log-badge--cancelled { background: #fef3c7; color: #92400e; }
|
|
10747
|
+
`;
|
|
10748
|
+
var ADMIN_CSS_BASE = `:root {
|
|
10544
10749
|
--tempest-bg: #0f172a;
|
|
10545
10750
|
--tempest-bg-soft: #1e293b;
|
|
10546
10751
|
--tempest-bg-row: #f8fafc;
|
|
@@ -12099,6 +12304,7 @@ a.tempest-admin-list__new:hover {
|
|
|
12099
12304
|
}
|
|
12100
12305
|
}
|
|
12101
12306
|
`;
|
|
12307
|
+
var ADMIN_CSS = `${ADMIN_CSS_BASE}${ADMIN_CSS_EXTRA}`;
|
|
12102
12308
|
|
|
12103
12309
|
// src/admin/theme.ts
|
|
12104
12310
|
var FORBIDDEN_CHARS = ["<", ">", "{", "}", '"'];
|
|
@@ -12359,11 +12565,11 @@ function renderFilter(filter) {
|
|
|
12359
12565
|
function renderListPage(context, view) {
|
|
12360
12566
|
const bulk = view.bulkActions.length > 0 && context.session !== null;
|
|
12361
12567
|
const checkColumn = bulk ? 1 : 0;
|
|
12362
|
-
const headers = view.columns.map((
|
|
12363
|
-
const state = view.sort[
|
|
12364
|
-
if (state === void 0) return `<th>${escapeHtml(
|
|
12568
|
+
const headers = view.columns.map((column7) => {
|
|
12569
|
+
const state = view.sort[column7];
|
|
12570
|
+
if (state === void 0) return `<th>${escapeHtml(column7)}</th>`;
|
|
12365
12571
|
const arrow = state.active ? state.ascending ? "\u25B2" : "\u25BC" : "\u2195";
|
|
12366
|
-
return `<th><a class="tempest-sort${state.active ? " tempest-sort--active" : ""}" href="${escapeHtml(state.url)}"><span>${escapeHtml(
|
|
12572
|
+
return `<th><a class="tempest-sort${state.active ? " tempest-sort--active" : ""}" href="${escapeHtml(state.url)}"><span>${escapeHtml(column7)}</span><span class="tempest-sort__arrow" aria-hidden="true">${arrow}</span></a></th>`;
|
|
12367
12573
|
}).join("");
|
|
12368
12574
|
const rows = view.rows.length > 0 ? view.rows.map((row) => {
|
|
12369
12575
|
const check = bulk ? `<td class="tempest-admin-list__check"><input type="checkbox" name="ids" value="${escapeHtml(row.identity)}" data-row-check aria-label="Select row"></td>` : "";
|
|
@@ -12504,7 +12710,7 @@ function renderInline(inline, csrfToken, error) {
|
|
|
12504
12710
|
if (inline.rows.length === 0) {
|
|
12505
12711
|
return `<section class="tempest-admin-inline">${heading}<p><em>No related records.</em></p></section>`;
|
|
12506
12712
|
}
|
|
12507
|
-
const head2 = `<tr>${inline.columns.map((
|
|
12713
|
+
const head2 = `<tr>${inline.columns.map((column7) => `<th>${escapeHtml(column7)}</th>`).join("")}<th></th></tr>`;
|
|
12508
12714
|
const body = inline.rows.map(
|
|
12509
12715
|
(row) => `<tr>${row.cells.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}<td>${row.url === null ? "" : `<a href="${escapeHtml(row.url)}">View</a>`}</td></tr>`
|
|
12510
12716
|
).join("");
|
|
@@ -12519,7 +12725,7 @@ function renderInline(inline, csrfToken, error) {
|
|
|
12519
12725
|
${more}
|
|
12520
12726
|
</section>`;
|
|
12521
12727
|
}
|
|
12522
|
-
const head = `<tr>${inline.columns.map((
|
|
12728
|
+
const head = `<tr>${inline.columns.map((column7) => `<th>${escapeHtml(column7)}</th>`).join("")}${inline.canDelete ? "<th>Delete</th>" : ""}</tr>`;
|
|
12523
12729
|
const renderRow = (row, isNew) => `<tr${isNew ? ' class="tempest-admin-inline__new"' : ""}>${row.fields.map((field) => `<td>${renderInlineCell(field)}</td>`).join("")}${inline.canDelete ? `<td class="tempest-admin-inline__del">${isNew ? "" : `<input type="checkbox" name="row.${escapeHtml(row.key)}.__delete" value="true">`}</td>` : ""}</tr>`;
|
|
12524
12730
|
const rows = inline.rows.map((row) => renderRow(row, false)).join("");
|
|
12525
12731
|
const blank = inline.newRow === null ? "" : renderRow(inline.newRow, true);
|
|
@@ -12690,12 +12896,86 @@ function renderLogsPage(context, view) {
|
|
|
12690
12896
|
</section>`;
|
|
12691
12897
|
return renderLayout(context, `Logs \xB7 ${context.site.title}`, body);
|
|
12692
12898
|
}
|
|
12899
|
+
function renderTasksPage(context, view) {
|
|
12900
|
+
const declared = view.inventory === null ? "" : `<section class="tempest-admin-tasks__declared">
|
|
12901
|
+
<h2>Declared</h2>
|
|
12902
|
+
${view.inventory.length === 0 ? "<p><em>No tasks registered in this process.</em></p>" : `<div class="tempest-admin-table-wrap"><table class="tempest-admin-list__table">
|
|
12903
|
+
<thead><tr><th>Task</th><th>Schedule</th><th>Description</th></tr></thead>
|
|
12904
|
+
<tbody>${view.inventory.map(
|
|
12905
|
+
(entry) => `<tr><td>${escapeHtml(entry.name)}</td><td>${escapeHtml(entry.schedule)}</td><td>${escapeHtml(entry.description)}</td></tr>`
|
|
12906
|
+
).join("")}</tbody>
|
|
12907
|
+
</table></div>`}
|
|
12908
|
+
</section>`;
|
|
12909
|
+
const runs = view.runs === null ? "" : `<section class="tempest-admin-tasks__runs">
|
|
12910
|
+
<h2>Runs</h2>
|
|
12911
|
+
<div class="tempest-admin-list__toolbar">
|
|
12912
|
+
<form method="get" class="tempest-admin-list__filters">
|
|
12913
|
+
<label><span>Status</span><select name="status">${view.runs.statuses.map(
|
|
12914
|
+
(status) => `<option value="${escapeHtml(status.value)}"${status.selected ? " selected" : ""}>${escapeHtml(status.label)}</option>`
|
|
12915
|
+
).join("")}</select></label>
|
|
12916
|
+
<input type="search" name="task" value="${escapeHtml(view.runs.nameQuery)}" placeholder="Task name\u2026" aria-label="Task name">
|
|
12917
|
+
<button type="submit">Apply</button>
|
|
12918
|
+
</form>
|
|
12919
|
+
</div>
|
|
12920
|
+
<div class="tempest-admin-table-wrap"><table class="tempest-admin-list__table">
|
|
12921
|
+
<thead><tr><th>Task</th><th>Status</th><th>Started</th><th>Finished</th><th>Attempts</th><th></th></tr></thead>
|
|
12922
|
+
<tbody>${view.runs.rows.length === 0 ? '<tr><td colspan="6">No runs recorded yet.</td></tr>' : view.runs.rows.map(
|
|
12923
|
+
(row) => `<tr>
|
|
12924
|
+
<td>${escapeHtml(row.name)}</td>
|
|
12925
|
+
<td><span class="tempest-log-badge tempest-log-badge--${escapeHtml(row.status)}">${escapeHtml(row.status)}</span></td>
|
|
12926
|
+
<td>${escapeHtml(row.startedAt)}</td>
|
|
12927
|
+
<td>${escapeHtml(row.finishedAt)}</td>
|
|
12928
|
+
<td>${escapeHtml(row.attempts)}</td>
|
|
12929
|
+
<td><a href="${escapeHtml(row.url)}">View</a></td>
|
|
12930
|
+
</tr>`
|
|
12931
|
+
).join("")}</tbody>
|
|
12932
|
+
</table></div>
|
|
12933
|
+
${view.runs.pages > 1 ? `<nav class="tempest-admin-list__pagination" aria-label="Pagination">
|
|
12934
|
+
${view.runs.prevUrl !== null ? `<a href="${escapeHtml(view.runs.prevUrl)}">\u2190 Prev</a>` : ""}
|
|
12935
|
+
<span>Page ${escapeHtml(view.runs.page)} of ${escapeHtml(view.runs.pages)}</span>
|
|
12936
|
+
${view.runs.nextUrl !== null ? `<a href="${escapeHtml(view.runs.nextUrl)}">Next \u2192</a>` : ""}
|
|
12937
|
+
</nav>` : ""}
|
|
12938
|
+
</section>`;
|
|
12939
|
+
const body = `<section class="tempest-admin-tasks">
|
|
12940
|
+
<header class="tempest-admin-list__header">
|
|
12941
|
+
<h1>Tasks</h1>
|
|
12942
|
+
<p>What this process declares, and what its workers recorded.</p>
|
|
12943
|
+
</header>
|
|
12944
|
+
${declared}
|
|
12945
|
+
${runs}
|
|
12946
|
+
<p class="tempest-admin-form__hint">Live queue depth is not shown: a broker does not expose it, and a number that looked like one would be worse than none.</p>
|
|
12947
|
+
</section>`;
|
|
12948
|
+
return renderLayout(context, `Tasks \xB7 ${context.site.title}`, body);
|
|
12949
|
+
}
|
|
12950
|
+
function renderTaskDetailPage(context, view) {
|
|
12951
|
+
if (context.session === null) throw new Error("The task detail requires a session");
|
|
12952
|
+
const block = (label, content) => content === null ? "" : `<h2>${escapeHtml(label)}</h2><pre class="tempest-admin-detail__json">${escapeHtml(content)}</pre>`;
|
|
12953
|
+
const body = `<section class="tempest-admin-detail">
|
|
12954
|
+
<header class="tempest-admin-detail__header">
|
|
12955
|
+
<h1>${escapeHtml(view.name)} \xB7 <span class="tempest-log-badge tempest-log-badge--${escapeHtml(view.status)}">${escapeHtml(view.status)}</span></h1>
|
|
12956
|
+
<div class="tempest-admin-detail__actions">
|
|
12957
|
+
<a href="${escapeHtml(view.backUrl)}">\u2190 Back to tasks</a>
|
|
12958
|
+
${view.cancelUrl !== null ? `<form method="post" action="${escapeHtml(view.cancelUrl)}" class="tempest-admin-detail__delete" onsubmit="return confirm('Ask this run to stop?');">
|
|
12959
|
+
<input type="hidden" name="csrf_token" value="${escapeHtml(context.session.csrfToken)}">
|
|
12960
|
+
<button type="submit" class="tempest-admin-btn--danger">Cancel</button>
|
|
12961
|
+
</form>` : ""}
|
|
12962
|
+
</div>
|
|
12963
|
+
</header>
|
|
12964
|
+
<dl class="tempest-admin-detail__fields">${view.fields.map(
|
|
12965
|
+
(field) => `<dt>${escapeHtml(field.label)}</dt><dd>${field.value === "" ? "<em>\u2014</em>" : escapeHtml(field.value)}</dd>`
|
|
12966
|
+
).join("")}</dl>
|
|
12967
|
+
${view.error === null ? "" : `<p class="tempest-admin-form__error">${escapeHtml(view.error)}</p>`}
|
|
12968
|
+
${block("Payload", view.payload)}
|
|
12969
|
+
${block("Result", view.result)}
|
|
12970
|
+
</section>`;
|
|
12971
|
+
return renderLayout(context, `${view.name} \xB7 ${context.site.title}`, body);
|
|
12972
|
+
}
|
|
12693
12973
|
function renderSqlPage(context, view) {
|
|
12694
12974
|
if (context.session === null) throw new Error("The SQL console requires a session");
|
|
12695
12975
|
const result = view.rowCount === null ? "" : `<p class="tempest-admin-form__hint">${escapeHtml(view.rowCount)} row${view.rowCount === 1 ? "" : "s"}${view.durationMs === null ? "" : ` in ${escapeHtml(view.durationMs)} ms`}${view.truncated ? " (truncated by the row cap)" : ""}.</p>
|
|
12696
12976
|
<div class="tempest-admin-table-wrap">
|
|
12697
12977
|
<table class="tempest-admin-list__table">
|
|
12698
|
-
<thead><tr>${view.columns.map((
|
|
12978
|
+
<thead><tr>${view.columns.map((column7) => `<th>${escapeHtml(column7)}</th>`).join("")}</tr></thead>
|
|
12699
12979
|
<tbody>${view.rows.map(
|
|
12700
12980
|
(row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`
|
|
12701
12981
|
).join("")}</tbody>
|
|
@@ -12865,6 +13145,15 @@ function makeLogsRouter(options) {
|
|
|
12865
13145
|
return router;
|
|
12866
13146
|
}
|
|
12867
13147
|
var logger2 = new JSONLogger("tempest_express_sdk.admin.router");
|
|
13148
|
+
var JOB_STATUSES = [
|
|
13149
|
+
"queued",
|
|
13150
|
+
"running",
|
|
13151
|
+
"succeeded",
|
|
13152
|
+
"failed",
|
|
13153
|
+
"cancelled"
|
|
13154
|
+
];
|
|
13155
|
+
var TERMINAL_JOB_STATUSES = ["succeeded", "failed", "cancelled"];
|
|
13156
|
+
var TASK_PAGE_SIZE = 25;
|
|
12868
13157
|
var LOG_SOURCES = ["all", "debug", "info", "warning", "error", "500"];
|
|
12869
13158
|
var LOG_PAGE_SIZE = 50;
|
|
12870
13159
|
var LOG_EXPORT_MAX = 500;
|
|
@@ -12906,6 +13195,9 @@ function makeAdminRouter(site, options) {
|
|
|
12906
13195
|
if (options.sqlConsole !== void 0) {
|
|
12907
13196
|
systemNav.push({ label: "SQL console", url: `${prefix}/sql` });
|
|
12908
13197
|
}
|
|
13198
|
+
if (options.tasks !== void 0) {
|
|
13199
|
+
systemNav.push({ label: "Tasks", url: `${prefix}/tasks` });
|
|
13200
|
+
}
|
|
12909
13201
|
const maxUploadBytes = options.maxUploadBytes ?? 10 * 1024 * 1024;
|
|
12910
13202
|
const sessions = new AdminSessionStore({
|
|
12911
13203
|
secret: options.secretKey,
|
|
@@ -13310,7 +13602,7 @@ function makeAdminRouter(site, options) {
|
|
|
13310
13602
|
error: null,
|
|
13311
13603
|
columns,
|
|
13312
13604
|
rows: window.map(
|
|
13313
|
-
(row) => columns.map((
|
|
13605
|
+
(row) => columns.map((column7) => formatCellValue(row[column7]))
|
|
13314
13606
|
),
|
|
13315
13607
|
rowCount: rows.length,
|
|
13316
13608
|
truncated: rows.length > window.length,
|
|
@@ -13319,6 +13611,115 @@ function makeAdminRouter(site, options) {
|
|
|
13319
13611
|
})
|
|
13320
13612
|
);
|
|
13321
13613
|
}
|
|
13614
|
+
if (options.tasks !== void 0) {
|
|
13615
|
+
const tasks = options.tasks;
|
|
13616
|
+
router.get(
|
|
13617
|
+
`${prefix}/tasks`,
|
|
13618
|
+
guarded(async (req, res) => {
|
|
13619
|
+
const state = await authenticate(req, res);
|
|
13620
|
+
if (state === null) return;
|
|
13621
|
+
const inventory = tasks.manager === void 0 ? null : tasks.manager.inventory().map((entry) => ({
|
|
13622
|
+
name: entry.name,
|
|
13623
|
+
description: entry.description ?? "",
|
|
13624
|
+
schedule: entry.schedule ?? "\u2014"
|
|
13625
|
+
}));
|
|
13626
|
+
let runs = null;
|
|
13627
|
+
if (tasks.jobs !== void 0) {
|
|
13628
|
+
const store = tasks.jobs(state.dbSession);
|
|
13629
|
+
const page2 = Math.max(1, Number.parseInt(queryString(req.query.page), 10) || 1);
|
|
13630
|
+
const status = queryString(req.query.status);
|
|
13631
|
+
const name = queryString(req.query.task);
|
|
13632
|
+
const result = await store.list({
|
|
13633
|
+
page: page2,
|
|
13634
|
+
pageSize: TASK_PAGE_SIZE,
|
|
13635
|
+
...name === "" ? {} : { name },
|
|
13636
|
+
...JOB_STATUSES.includes(status) ? { status } : {}
|
|
13637
|
+
});
|
|
13638
|
+
const pageUrl = (target) => `?${buildQuery({ status, task: name, page: target })}`;
|
|
13639
|
+
runs = {
|
|
13640
|
+
rows: result.items.map((row) => ({
|
|
13641
|
+
id: String(row.id),
|
|
13642
|
+
name: String(row.name ?? ""),
|
|
13643
|
+
status: String(row.status ?? ""),
|
|
13644
|
+
startedAt: formatCellValue(row.startedAt),
|
|
13645
|
+
finishedAt: formatCellValue(row.finishedAt),
|
|
13646
|
+
attempts: formatCellValue(row.attempts),
|
|
13647
|
+
url: `${prefix}/tasks/${String(row.id)}`
|
|
13648
|
+
})),
|
|
13649
|
+
total: result.total,
|
|
13650
|
+
page: result.page,
|
|
13651
|
+
pages: result.pages,
|
|
13652
|
+
prevUrl: result.page > 1 ? pageUrl(result.page - 1) : null,
|
|
13653
|
+
nextUrl: result.page < result.pages ? pageUrl(result.page + 1) : null,
|
|
13654
|
+
statuses: ["", ...JOB_STATUSES].map((value) => ({
|
|
13655
|
+
value,
|
|
13656
|
+
label: value === "" ? "\u2014 any \u2014" : humanizeField(value),
|
|
13657
|
+
selected: value === status
|
|
13658
|
+
})),
|
|
13659
|
+
nameQuery: name
|
|
13660
|
+
};
|
|
13661
|
+
}
|
|
13662
|
+
html(
|
|
13663
|
+
res,
|
|
13664
|
+
renderTasksPage(context(req, state.session, state.visible), {
|
|
13665
|
+
inventory,
|
|
13666
|
+
runs
|
|
13667
|
+
})
|
|
13668
|
+
);
|
|
13669
|
+
})
|
|
13670
|
+
);
|
|
13671
|
+
if (tasks.jobs !== void 0) {
|
|
13672
|
+
const jobs = tasks.jobs;
|
|
13673
|
+
router.get(
|
|
13674
|
+
`${prefix}/tasks/:id`,
|
|
13675
|
+
guarded(async (req, res) => {
|
|
13676
|
+
const state = await authenticate(req, res);
|
|
13677
|
+
if (state === null) return;
|
|
13678
|
+
const row = await jobs(state.dbSession).get(String(req.params.id));
|
|
13679
|
+
if (row === null) {
|
|
13680
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
13681
|
+
return;
|
|
13682
|
+
}
|
|
13683
|
+
const status = String(row.status ?? "");
|
|
13684
|
+
html(
|
|
13685
|
+
res,
|
|
13686
|
+
renderTaskDetailPage(context(req, state.session, state.visible), {
|
|
13687
|
+
id: String(row.id),
|
|
13688
|
+
name: String(row.name ?? ""),
|
|
13689
|
+
status,
|
|
13690
|
+
fields: [
|
|
13691
|
+
{ label: "Created At", value: formatCellValue(row.createdAt) },
|
|
13692
|
+
{ label: "Started At", value: formatCellValue(row.startedAt) },
|
|
13693
|
+
{ label: "Finished At", value: formatCellValue(row.finishedAt) },
|
|
13694
|
+
{ label: "Attempts", value: formatCellValue(row.attempts) }
|
|
13695
|
+
],
|
|
13696
|
+
payload: jsonBlock(row.payload),
|
|
13697
|
+
result: jsonBlock(row.result),
|
|
13698
|
+
error: typeof row.error === "string" && row.error !== "" ? row.error : null,
|
|
13699
|
+
backUrl: `${prefix}/tasks`,
|
|
13700
|
+
cancelUrl: TERMINAL_JOB_STATUSES.includes(status) ? null : `${prefix}/tasks/${String(row.id)}/cancel`
|
|
13701
|
+
})
|
|
13702
|
+
);
|
|
13703
|
+
})
|
|
13704
|
+
);
|
|
13705
|
+
router.post(
|
|
13706
|
+
`${prefix}/tasks/:id/cancel`,
|
|
13707
|
+
guarded(async (req, res) => {
|
|
13708
|
+
const state = await authenticate(req, res);
|
|
13709
|
+
if (state === null) return;
|
|
13710
|
+
if (!checkCsrf(req, res, state)) return;
|
|
13711
|
+
const id = String(req.params.id);
|
|
13712
|
+
const cancelled = await jobs(state.dbSession).cancel(id);
|
|
13713
|
+
res.redirect(
|
|
13714
|
+
`${prefix}/tasks/${id}?${buildQuery({
|
|
13715
|
+
flash: cancelled ? "The run was asked to stop." : "That run had already finished.",
|
|
13716
|
+
level: cancelled ? "success" : "warning"
|
|
13717
|
+
})}`
|
|
13718
|
+
);
|
|
13719
|
+
})
|
|
13720
|
+
);
|
|
13721
|
+
}
|
|
13722
|
+
}
|
|
13322
13723
|
router.get(
|
|
13323
13724
|
`${prefix}/m/:slug`,
|
|
13324
13725
|
guarded(async (req, res) => {
|
|
@@ -13629,8 +14030,8 @@ function makeAdminRouter(site, options) {
|
|
|
13629
14030
|
res.status(404).json({ options: [] });
|
|
13630
14031
|
return;
|
|
13631
14032
|
}
|
|
13632
|
-
const
|
|
13633
|
-
const table =
|
|
14033
|
+
const column7 = adminColumns(admin.model)[field];
|
|
14034
|
+
const table = column7 === void 0 ? null : foreignKeyTable(column7);
|
|
13634
14035
|
const referenced = table === null ? null : site.get(table);
|
|
13635
14036
|
if (referenced === null) {
|
|
13636
14037
|
res.json({ options: [] });
|
|
@@ -13939,7 +14340,7 @@ function makeAdminRouter(site, options) {
|
|
|
13939
14340
|
formAction: "",
|
|
13940
14341
|
rows: visible.map((child) => ({
|
|
13941
14342
|
key: String(child[childAdmin?.identityField ?? "id"]),
|
|
13942
|
-
cells: columns.map((
|
|
14343
|
+
cells: columns.map((column7) => formatCellValue(child[column7])),
|
|
13943
14344
|
fields: [],
|
|
13944
14345
|
url: childAdmin === null ? null : `${prefix}/m/${inline.slug}/${String(child[childAdmin.identityField])}`
|
|
13945
14346
|
})),
|
|
@@ -14071,8 +14472,8 @@ function makeAdminRouter(site, options) {
|
|
|
14071
14472
|
for (const field of admin.uploadFields) {
|
|
14072
14473
|
const file = files.find((candidate) => candidate.field === field);
|
|
14073
14474
|
if (file === void 0) {
|
|
14074
|
-
const
|
|
14075
|
-
if (creating &&
|
|
14475
|
+
const column7 = columns[field];
|
|
14476
|
+
if (creating && column7 !== void 0 && !isColumnOptional(column7)) {
|
|
14076
14477
|
errors[field] = "This field is required.";
|
|
14077
14478
|
}
|
|
14078
14479
|
continue;
|
|
@@ -14104,14 +14505,14 @@ function makeAdminRouter(site, options) {
|
|
|
14104
14505
|
});
|
|
14105
14506
|
const displayed = admin.listDisplayNames();
|
|
14106
14507
|
const sort = {};
|
|
14107
|
-
for (const
|
|
14108
|
-
if (!(
|
|
14109
|
-
const active = (query.sortColumn ?? admin.orderKey) ===
|
|
14508
|
+
for (const column7 of displayed) {
|
|
14509
|
+
if (!(column7 in columns)) continue;
|
|
14510
|
+
const active = (query.sortColumn ?? admin.orderKey) === column7;
|
|
14110
14511
|
const nextAscending = active ? !query.ascending : true;
|
|
14111
|
-
sort[
|
|
14512
|
+
sort[column7] = {
|
|
14112
14513
|
url: `?${buildQuery({
|
|
14113
14514
|
...query.baseQuery,
|
|
14114
|
-
sort:
|
|
14515
|
+
sort: column7,
|
|
14115
14516
|
dir: nextAscending ? "asc" : "desc"
|
|
14116
14517
|
})}`,
|
|
14117
14518
|
active,
|
|
@@ -14132,7 +14533,7 @@ function makeAdminRouter(site, options) {
|
|
|
14132
14533
|
const identity = String(row[admin.identityField]);
|
|
14133
14534
|
return {
|
|
14134
14535
|
identity,
|
|
14135
|
-
cells: displayed.map((
|
|
14536
|
+
cells: displayed.map((column7) => formatCellValue(row[column7])),
|
|
14136
14537
|
url: `${prefix}/m/${admin.slug()}/${identity}`
|
|
14137
14538
|
};
|
|
14138
14539
|
}),
|
|
@@ -14178,9 +14579,9 @@ function makeAdminRouter(site, options) {
|
|
|
14178
14579
|
}
|
|
14179
14580
|
const filterViews = [];
|
|
14180
14581
|
for (const field of admin.listFilter) {
|
|
14181
|
-
const
|
|
14182
|
-
if (
|
|
14183
|
-
const spec = filterForColumn(
|
|
14582
|
+
const column7 = columns[field];
|
|
14583
|
+
if (column7 === void 0) continue;
|
|
14584
|
+
const spec = filterForColumn(column7);
|
|
14184
14585
|
if (spec.kind === "daterange") {
|
|
14185
14586
|
const from = queryString(req.query[`filter_${field}_from`]);
|
|
14186
14587
|
const to = queryString(req.query[`filter_${field}_to`]);
|
|
@@ -14199,12 +14600,12 @@ function makeAdminRouter(site, options) {
|
|
|
14199
14600
|
});
|
|
14200
14601
|
continue;
|
|
14201
14602
|
}
|
|
14202
|
-
const related = await relatedOptions(
|
|
14603
|
+
const related = await relatedOptions(column7, dbSession);
|
|
14203
14604
|
const options2 = related ?? spec.options;
|
|
14204
14605
|
const value = queryString(req.query[`filter_${field}`]);
|
|
14205
14606
|
if (value !== "") {
|
|
14206
14607
|
conditions.push({
|
|
14207
|
-
[field]:
|
|
14608
|
+
[field]: column7.type.kind === "boolean" ? value === "true" : value
|
|
14208
14609
|
});
|
|
14209
14610
|
}
|
|
14210
14611
|
filterViews.push({
|
|
@@ -14222,8 +14623,8 @@ function makeAdminRouter(site, options) {
|
|
|
14222
14623
|
});
|
|
14223
14624
|
}
|
|
14224
14625
|
const searchable = admin.searchFields.filter((field) => {
|
|
14225
|
-
const
|
|
14226
|
-
return
|
|
14626
|
+
const column7 = columns[field];
|
|
14627
|
+
return column7 !== void 0 && isSearchableColumn(column7);
|
|
14227
14628
|
});
|
|
14228
14629
|
if (search !== "" && searchable.length > 0) {
|
|
14229
14630
|
conditions.push(
|
|
@@ -14256,8 +14657,8 @@ function makeAdminRouter(site, options) {
|
|
|
14256
14657
|
lens: lens?.slug ?? ""
|
|
14257
14658
|
};
|
|
14258
14659
|
}
|
|
14259
|
-
async function relatedOptions(
|
|
14260
|
-
const table = foreignKeyTable(
|
|
14660
|
+
async function relatedOptions(column7, dbSession) {
|
|
14661
|
+
const table = foreignKeyTable(column7);
|
|
14261
14662
|
if (table === null) return null;
|
|
14262
14663
|
const referenced = site.get(table);
|
|
14263
14664
|
if (referenced === null) return null;
|
|
@@ -14272,9 +14673,9 @@ function makeAdminRouter(site, options) {
|
|
|
14272
14673
|
const options2 = {};
|
|
14273
14674
|
for (const field of Object.keys(foreignKeyFields(admin))) {
|
|
14274
14675
|
if (admin.autocompleteFields.includes(field)) continue;
|
|
14275
|
-
const
|
|
14276
|
-
if (
|
|
14277
|
-
const related = await relatedOptions(
|
|
14676
|
+
const column7 = columns[field];
|
|
14677
|
+
if (column7 === void 0) continue;
|
|
14678
|
+
const related = await relatedOptions(column7, dbSession);
|
|
14278
14679
|
if (related !== null) options2[field] = related;
|
|
14279
14680
|
}
|
|
14280
14681
|
return options2;
|
|
@@ -14293,8 +14694,8 @@ function makeAdminRouter(site, options) {
|
|
|
14293
14694
|
for (const field of admin.autocompleteFields) {
|
|
14294
14695
|
const value = row[field];
|
|
14295
14696
|
if (value === null || value === void 0 || value === "") continue;
|
|
14296
|
-
const
|
|
14297
|
-
const table =
|
|
14697
|
+
const column7 = columns[field];
|
|
14698
|
+
const table = column7 === void 0 ? null : foreignKeyTable(column7);
|
|
14298
14699
|
const referenced = table === null ? null : site.get(table);
|
|
14299
14700
|
if (referenced === null) continue;
|
|
14300
14701
|
const related = await referenced.repository(dbSession).first({ [referenced.identityField]: value });
|
|
@@ -14333,6 +14734,11 @@ function inlineFieldNames(childAdmin, inline) {
|
|
|
14333
14734
|
function inlineFields(childAdmin, names, key, values, errors) {
|
|
14334
14735
|
return buildFormFields(childAdmin, { values, errors }).filter((field) => names.includes(field.name)).map((field) => ({ ...field, name: `row.${key}.${field.name}` }));
|
|
14335
14736
|
}
|
|
14737
|
+
function jsonBlock(value) {
|
|
14738
|
+
if (value === null || value === void 0) return null;
|
|
14739
|
+
if (typeof value === "object" && Object.keys(value).length === 0) return null;
|
|
14740
|
+
return JSON.stringify(value, null, 2);
|
|
14741
|
+
}
|
|
14336
14742
|
async function audit(hook, entry) {
|
|
14337
14743
|
if (hook === void 0) return;
|
|
14338
14744
|
try {
|
|
@@ -14496,7 +14902,7 @@ function csvField(value) {
|
|
|
14496
14902
|
function toCsv(columns, rows) {
|
|
14497
14903
|
const lines = [columns.map(csvField).join(",")];
|
|
14498
14904
|
for (const row of rows) {
|
|
14499
|
-
lines.push(columns.map((
|
|
14905
|
+
lines.push(columns.map((column7) => csvField(exportValue(row[column7]))).join(","));
|
|
14500
14906
|
}
|
|
14501
14907
|
return `${lines.join("\r\n")}\r
|
|
14502
14908
|
`;
|
|
@@ -14504,7 +14910,7 @@ function toCsv(columns, rows) {
|
|
|
14504
14910
|
function toJson(columns, rows) {
|
|
14505
14911
|
return JSON.stringify(
|
|
14506
14912
|
rows.map(
|
|
14507
|
-
(row) => Object.fromEntries(columns.map((
|
|
14913
|
+
(row) => Object.fromEntries(columns.map((column7) => [column7, exportValue(row[column7])]))
|
|
14508
14914
|
),
|
|
14509
14915
|
null,
|
|
14510
14916
|
2
|
|
@@ -16460,6 +16866,6 @@ async function withTestDatabase(models, fn) {
|
|
|
16460
16866
|
}
|
|
16461
16867
|
}
|
|
16462
16868
|
|
|
16463
|
-
export { ADMIN_CSS, ActivationService, AdminJsonSite, AdminModel, AdminPermission, AdminSessionStore, AdminSite, AppException, AttemptThrottle, AuditAction, BaseAuditLogModel, BaseController, BaseModel, BaseOAuthClient, BaseOutboxModel, BaseService, BaseUserModel, BaseUserRefreshTokenModel, BaseUserTokenModel, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_DOCS_FAVICON, DEFAULT_LOCALE, EmailProvider, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, GitHubOAuthClient, GoogleOAuthClient, GracefulShutdown, HTTPClient, HTTP_500_LOG_FILE, HTTP_500_MARKER, HttpMetrics, IDEMPOTENCY_HEADER, InvalidTokenException, JSONLogger, JWTUtils, LEVEL_LOG_FILES, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemoryIdempotencyStore, MemoryRateLimitStore, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MessagingHub, MetricsUtils, MfaService, MultipartLimitError, NotFoundException, OAuthError, OIDCProvider, OutboxRelay, OutboxStatus, PHONE_BR_PATTERN, PasswordResetService, REDOC_CDN_URL, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, RedisIdempotencyStore, RedisRateLimitStore, RedisSSEBroker, RedisSessionStore, Region, RetryPolicy, S3UploadStorage, SSEBroker, ServerSentEvent, SessionService, SqlCapability, TOTPHelper, TaskManager, TelegramProvider, TenantScopedRepository, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, UserModelAuthBackend, UserTokenPurpose, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WebhookSignatureVerifier, WhatsAppProvider, activationSchema, addLogSink, adminAction, adminColumns, adminInline, adminLens, adminThemeCss, analyzeSql, attachWebSocketHub, authResponseSchema, authSettingsShape, backupDatabase, bearerToken, bodySizeLimitMiddleware, broadcastText, buildContentDisposition, buildFormFields, buildPaginationLinkHeader, cached2 as cached, cepField, checkSqlPolicy, citiesByUf, cnpjField, coerceFlag, configureFileLogging, configureLogging, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createTestDatabase, createdByColumn, csrfMiddleware, csrfTokenMatches, cursorPaginationFilterSchema, cursorPaginationSchema, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, diffSnapshots, emailSettingsShape, encodeCursor, envList, escapeHtml, filterForColumn, filterLogEntries, foreignKeyFields, foreignKeyLabel, foreignKeyTable, formatCellValue, formatFieldValue, generateCsrfToken, generateOAuthState, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, groupInlineSubmission, hashOpaqueToken, humanizeField, idempotencyMiddleware, inboundMessageSchema, isColumnOptional, isMultipart, isSearchableColumn, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, jwtSettingsShape, keyByHeader, keyByIp, keyByJwtClaim, keyByJwtSubject, listStates, loadSqlParser, logEntrySchema, logSettingsShape, loginSchema, makeAdminJsonRouter, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeLogsRouter, makeMetricsRouter, makeSessionMiddleware, makeToolSpecRouter, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, metricCard, mfaChallengeSchema, mfaCodeSchema, mfaEnrollResponseSchema, minioSettingsShape, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, parseCsv, parseFormBody, parseMultipart, partitionTotal, passwordResetConfirmSchema, passwordResetRequestSchema, phoneBrField, prometheusMiddleware, rabbitmqSettingsShape, rateLimitMiddleware, readLogEntries, redisSettingsShape, refreshSchema, registerExceptionHandlers, renderAuthResultPage, renderDashboardPage, renderDetailPage, renderFormPage, renderImportPage, renderLayout, renderListPage, renderLogEntriesJson, renderLogEntriesMarkdown, renderLoginPage, renderLogsPage, renderMfaPage, renderPasswordResetFormPage, renderSqlPage, requestIdMiddleware, requestTracingMiddleware, requireRoles, resolveAdminTheme, resolveDownloadPath, resolveRedocBundle, runServer, runWithRequestContext, sendBytesDownload, sendFileDownload, sessionCookie, sessionSettingsShape, setRequestId, signupSchema, snapshot, sseResponse, statesByRegion, syncFilterSchema, syncPaginationSchema, tableNameFor, toLogEntry, toUtc, tokenFromUrl, tokenPairSchema, tokenSettingsShape, trendDirection, trendPercent, ufField, updatedByColumn, uploadSettingsShape, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSettingsShape, webPushSubscriptionSchema, webSocketSettingsShape, widgetForColumn, withTestDatabase, wrapWithSlowQueryLog, wsEnvelopeSchema };
|
|
16869
|
+
export { ADMIN_CSS, ActivationService, AdminJsonSite, AdminModel, AdminPermission, AdminSessionStore, AdminSite, AppException, AttemptThrottle, AuditAction, BaseAuditLogModel, BaseController, BaseJobModel, BaseModel, BaseOAuthClient, BaseOutboxModel, BaseService, BaseUserModel, BaseUserRefreshTokenModel, BaseUserTokenModel, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_DOCS_FAVICON, DEFAULT_LOCALE, EmailProvider, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, GitHubOAuthClient, GoogleOAuthClient, GracefulShutdown, HTTPClient, HTTP_500_LOG_FILE, HTTP_500_MARKER, HttpMetrics, IDEMPOTENCY_HEADER, InvalidTokenException, JSONLogger, JWTUtils, JobStatus, JobStore, LEVEL_LOG_FILES, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemoryIdempotencyStore, MemoryRateLimitStore, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MessagingHub, MetricsUtils, MfaService, MultipartLimitError, NotFoundException, OAuthError, OIDCProvider, OutboxRelay, OutboxStatus, PHONE_BR_PATTERN, PasswordResetService, REDOC_CDN_URL, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, RedisIdempotencyStore, RedisRateLimitStore, RedisSSEBroker, RedisSessionStore, Region, RetryPolicy, S3UploadStorage, SSEBroker, ServerSentEvent, SessionService, SqlCapability, TOTPHelper, TaskManager, TelegramProvider, TenantScopedRepository, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, UserModelAuthBackend, UserTokenPurpose, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WebhookSignatureVerifier, WhatsAppProvider, activationSchema, addLogSink, adminAction, adminColumns, adminInline, adminLens, adminThemeCss, analyzeSql, attachWebSocketHub, authResponseSchema, authSettingsShape, backupDatabase, bearerToken, bodySizeLimitMiddleware, broadcastText, buildContentDisposition, buildFormFields, buildPaginationLinkHeader, cached2 as cached, cepField, checkSqlPolicy, citiesByUf, cnpjField, coerceFlag, configureFileLogging, configureLogging, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createTestDatabase, createdByColumn, csrfMiddleware, csrfTokenMatches, cursorPaginationFilterSchema, cursorPaginationSchema, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, diffSnapshots, emailSettingsShape, encodeCursor, envList, escapeHtml, filterForColumn, filterLogEntries, foreignKeyFields, foreignKeyLabel, foreignKeyTable, formatCellValue, formatFieldValue, generateCsrfToken, generateOAuthState, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, groupInlineSubmission, hashOpaqueToken, humanizeField, idempotencyMiddleware, inboundMessageSchema, isColumnOptional, isMultipart, isSearchableColumn, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, jwtSettingsShape, keyByHeader, keyByIp, keyByJwtClaim, keyByJwtSubject, listStates, loadSqlParser, logEntrySchema, logSettingsShape, loginSchema, makeAdminJsonRouter, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeLogsRouter, makeMetricsRouter, makeSessionMiddleware, makeToolSpecRouter, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, metricCard, mfaChallengeSchema, mfaCodeSchema, mfaEnrollResponseSchema, minioSettingsShape, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, parseCsv, parseFormBody, parseMultipart, partitionTotal, passwordResetConfirmSchema, passwordResetRequestSchema, phoneBrField, prometheusMiddleware, rabbitmqSettingsShape, rateLimitMiddleware, readLogEntries, redisSettingsShape, refreshSchema, registerExceptionHandlers, renderAuthResultPage, renderDashboardPage, renderDetailPage, renderFormPage, renderImportPage, renderLayout, renderListPage, renderLogEntriesJson, renderLogEntriesMarkdown, renderLoginPage, renderLogsPage, renderMfaPage, renderPasswordResetFormPage, renderSqlPage, renderTaskDetailPage, renderTasksPage, requestIdMiddleware, requestTracingMiddleware, requireRoles, resolveAdminTheme, resolveDownloadPath, resolveRedocBundle, runServer, runWithRequestContext, sendBytesDownload, sendFileDownload, sessionCookie, sessionSettingsShape, setRequestId, signupSchema, snapshot, sseResponse, statesByRegion, syncFilterSchema, syncPaginationSchema, tableNameFor, toLogEntry, toUtc, tokenFromUrl, tokenPairSchema, tokenSettingsShape, trendDirection, trendPercent, ufField, updatedByColumn, uploadSettingsShape, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSettingsShape, webPushSubscriptionSchema, webSocketSettingsShape, widgetForColumn, withTestDatabase, wrapWithSlowQueryLog, wsEnvelopeSchema };
|
|
16464
16870
|
//# sourceMappingURL=index.js.map
|
|
16465
16871
|
//# sourceMappingURL=index.js.map
|