@tolinax/ayoune-cli 2026.2.2 → 2026.2.4
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/lib/commands/createBatchCommand.js +304 -0
- package/lib/commands/createCompletionsCommand.js +34 -1
- package/lib/commands/createDeleteCommand.js +98 -0
- package/lib/commands/createExportCommand.js +219 -0
- package/lib/commands/createJobsCommand.js +168 -0
- package/lib/commands/createPermissionsCommand.js +241 -0
- package/lib/commands/createProgram.js +22 -0
- package/lib/commands/createSearchCommand.js +101 -0
- package/lib/commands/createSyncCommand.js +177 -0
- package/lib/commands/createTemplateCommand.js +238 -0
- package/lib/commands/createUpdateCommand.js +115 -0
- package/lib/commands/createUsersCommand.js +285 -0
- package/lib/commands/createWebhooksCommand.js +156 -0
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
|
|
2
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
3
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
4
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
5
|
+
import { spinner } from "../../index.js";
|
|
6
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
7
|
+
export function createExportCommand(program) {
|
|
8
|
+
const exp = program
|
|
9
|
+
.command("export")
|
|
10
|
+
.alias("exp")
|
|
11
|
+
.description("Export data from collections in various formats");
|
|
12
|
+
// ay export run <collection>
|
|
13
|
+
exp
|
|
14
|
+
.command("run <collection>")
|
|
15
|
+
.description("Export entries from a collection")
|
|
16
|
+
.addHelpText("after", `
|
|
17
|
+
Examples:
|
|
18
|
+
ay export run contacts --format csv --fields "firstName,lastName,email"
|
|
19
|
+
ay export run products --format json --filter "status=active"
|
|
20
|
+
ay export run invoices --format csv --filter "createdAt>2025-01-01" --save`)
|
|
21
|
+
.option("--format <format>", "Export format (json, csv, yaml)", "csv")
|
|
22
|
+
.option("--fields <fields>", "Comma-separated fields to include in export")
|
|
23
|
+
.option("--filter <filters>", "Comma-separated key=value filters")
|
|
24
|
+
.option("--sort <field>", "Sort by field (prefix with - for descending)", "-createdAt")
|
|
25
|
+
.option("-l, --limit <number>", "Limit results (0 = all)", parseInt, 0)
|
|
26
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
27
|
+
.action(async (collection, options) => {
|
|
28
|
+
var _a, _b, _c, _d, _e, _f;
|
|
29
|
+
try {
|
|
30
|
+
const opts = { ...program.opts(), ...options };
|
|
31
|
+
const module = getModuleFromCollection(collection);
|
|
32
|
+
spinner.start({ text: `Exporting ${collection}...`, color: "magenta" });
|
|
33
|
+
const params = {
|
|
34
|
+
page: opts.page,
|
|
35
|
+
sort: opts.sort,
|
|
36
|
+
responseFormat: opts.format,
|
|
37
|
+
verbosity: opts.verbosity,
|
|
38
|
+
};
|
|
39
|
+
if (opts.limit > 0)
|
|
40
|
+
params.limit = opts.limit;
|
|
41
|
+
if (opts.fields)
|
|
42
|
+
params.fields = opts.fields;
|
|
43
|
+
// Parse filters
|
|
44
|
+
if (opts.filter) {
|
|
45
|
+
const filters = opts.filter.split(",");
|
|
46
|
+
for (const f of filters) {
|
|
47
|
+
const match = f.match(/^(\w+)(!=|>=|<=|>|<|=)(.+)$/);
|
|
48
|
+
if (match) {
|
|
49
|
+
const [, key, op, value] = match;
|
|
50
|
+
if (op === "=")
|
|
51
|
+
params[key] = value;
|
|
52
|
+
else if (op === "!=")
|
|
53
|
+
params[`${key}[ne]`] = value;
|
|
54
|
+
else if (op === ">")
|
|
55
|
+
params[`${key}[gt]`] = value;
|
|
56
|
+
else if (op === "<")
|
|
57
|
+
params[`${key}[lt]`] = value;
|
|
58
|
+
else if (op === ">=")
|
|
59
|
+
params[`${key}[gte]`] = value;
|
|
60
|
+
else if (op === "<=")
|
|
61
|
+
params[`${key}[lte]`] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// If limit=0, fetch all pages
|
|
66
|
+
if (opts.limit === 0) {
|
|
67
|
+
params.limit = 500;
|
|
68
|
+
const allPayload = [];
|
|
69
|
+
let currentPage = 1;
|
|
70
|
+
let totalPages = 1;
|
|
71
|
+
do {
|
|
72
|
+
params.page = currentPage;
|
|
73
|
+
const res = await apiCallHandler(module.module, collection.toLowerCase(), "get", null, params);
|
|
74
|
+
if (res === null || res === void 0 ? void 0 : res.payload) {
|
|
75
|
+
if (Array.isArray(res.payload))
|
|
76
|
+
allPayload.push(...res.payload);
|
|
77
|
+
else
|
|
78
|
+
allPayload.push(res.payload);
|
|
79
|
+
}
|
|
80
|
+
totalPages = (_c = (_b = (_a = res === null || res === void 0 ? void 0 : res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalPages) !== null && _c !== void 0 ? _c : 1;
|
|
81
|
+
spinner.update({ text: `Exporting ${collection}... page ${currentPage}/${totalPages}` });
|
|
82
|
+
currentPage++;
|
|
83
|
+
} while (currentPage <= totalPages);
|
|
84
|
+
const fullRes = {
|
|
85
|
+
payload: allPayload,
|
|
86
|
+
meta: { pageInfo: { totalEntries: allPayload.length, page: 1, totalPages: 1 } },
|
|
87
|
+
};
|
|
88
|
+
// Force response format to match export format
|
|
89
|
+
opts.responseFormat = opts.format;
|
|
90
|
+
handleResponseFormatOptions(opts, fullRes);
|
|
91
|
+
spinner.success({ text: `Exported ${allPayload.length} entries from ${collection}` });
|
|
92
|
+
spinner.stop();
|
|
93
|
+
if (opts.save)
|
|
94
|
+
await saveFile(`export-${collection}`, opts, fullRes);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const res = await apiCallHandler(module.module, collection.toLowerCase(), "get", null, params);
|
|
98
|
+
opts.responseFormat = opts.format;
|
|
99
|
+
handleResponseFormatOptions(opts, res);
|
|
100
|
+
const total = (_f = (_e = (_d = res.meta) === null || _d === void 0 ? void 0 : _d.pageInfo) === null || _e === void 0 ? void 0 : _e.totalEntries) !== null && _f !== void 0 ? _f : 0;
|
|
101
|
+
spinner.success({ text: `Exported ${total} entries from ${collection}` });
|
|
102
|
+
spinner.stop();
|
|
103
|
+
if (opts.save)
|
|
104
|
+
await saveFile(`export-${collection}`, opts, res);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
spinner.error({ text: e.message || "Export failed" });
|
|
109
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// ay export list
|
|
113
|
+
exp
|
|
114
|
+
.command("list")
|
|
115
|
+
.alias("ls")
|
|
116
|
+
.description("List previous exports")
|
|
117
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 25)
|
|
118
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
119
|
+
.action(async (options) => {
|
|
120
|
+
var _a, _b, _c;
|
|
121
|
+
try {
|
|
122
|
+
const opts = { ...program.opts(), ...options };
|
|
123
|
+
spinner.start({ text: "Fetching exports...", color: "magenta" });
|
|
124
|
+
const res = await apiCallHandler("export", "exports", "get", null, {
|
|
125
|
+
page: opts.page,
|
|
126
|
+
limit: opts.limit,
|
|
127
|
+
responseFormat: opts.responseFormat,
|
|
128
|
+
verbosity: opts.verbosity,
|
|
129
|
+
});
|
|
130
|
+
handleResponseFormatOptions(opts, res);
|
|
131
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
132
|
+
spinner.success({ text: `Found ${total} exports` });
|
|
133
|
+
spinner.stop();
|
|
134
|
+
if (opts.save)
|
|
135
|
+
await saveFile("exports-list", opts, res);
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
spinner.error({ text: e.message || "Failed to list exports" });
|
|
139
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
// ay export get <id>
|
|
143
|
+
exp
|
|
144
|
+
.command("get <id>")
|
|
145
|
+
.description("Get export details and download URL")
|
|
146
|
+
.action(async (id, options) => {
|
|
147
|
+
try {
|
|
148
|
+
const opts = { ...program.opts(), ...options };
|
|
149
|
+
spinner.start({ text: `Fetching export ${id}...`, color: "magenta" });
|
|
150
|
+
const res = await apiCallHandler("export", `exports/${id}`, "get", null, {
|
|
151
|
+
responseFormat: opts.responseFormat,
|
|
152
|
+
verbosity: opts.verbosity,
|
|
153
|
+
});
|
|
154
|
+
handleResponseFormatOptions(opts, res);
|
|
155
|
+
spinner.success({ text: `Export ${id} loaded` });
|
|
156
|
+
spinner.stop();
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
spinner.error({ text: e.message || "Failed to get export" });
|
|
160
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// ay export configs
|
|
164
|
+
exp
|
|
165
|
+
.command("configs")
|
|
166
|
+
.description("List export configurations")
|
|
167
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
168
|
+
.action(async (options) => {
|
|
169
|
+
var _a, _b, _c;
|
|
170
|
+
try {
|
|
171
|
+
const opts = { ...program.opts(), ...options };
|
|
172
|
+
spinner.start({ text: "Fetching export configs...", color: "magenta" });
|
|
173
|
+
const res = await apiCallHandler("export", "exportconfigs", "get", null, {
|
|
174
|
+
limit: opts.limit,
|
|
175
|
+
responseFormat: opts.responseFormat,
|
|
176
|
+
verbosity: opts.verbosity,
|
|
177
|
+
});
|
|
178
|
+
handleResponseFormatOptions(opts, res);
|
|
179
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
180
|
+
spinner.success({ text: `Found ${total} export configurations` });
|
|
181
|
+
spinner.stop();
|
|
182
|
+
if (opts.save)
|
|
183
|
+
await saveFile("export-configs", opts, res);
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
spinner.error({ text: e.message || "Failed to list export configs" });
|
|
187
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
// ay export logs
|
|
191
|
+
exp
|
|
192
|
+
.command("logs")
|
|
193
|
+
.description("List export logs")
|
|
194
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 25)
|
|
195
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
196
|
+
.action(async (options) => {
|
|
197
|
+
var _a, _b, _c;
|
|
198
|
+
try {
|
|
199
|
+
const opts = { ...program.opts(), ...options };
|
|
200
|
+
spinner.start({ text: "Fetching export logs...", color: "magenta" });
|
|
201
|
+
const res = await apiCallHandler("logs", "export", "get", null, {
|
|
202
|
+
page: opts.page,
|
|
203
|
+
limit: opts.limit,
|
|
204
|
+
responseFormat: opts.responseFormat,
|
|
205
|
+
verbosity: opts.verbosity,
|
|
206
|
+
});
|
|
207
|
+
handleResponseFormatOptions(opts, res);
|
|
208
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
209
|
+
spinner.success({ text: `Found ${total} export log entries` });
|
|
210
|
+
spinner.stop();
|
|
211
|
+
if (opts.save)
|
|
212
|
+
await saveFile("export-logs", opts, res);
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
spinner.error({ text: e.message || "Failed to list export logs" });
|
|
216
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
2
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
3
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
4
|
+
import { spinner } from "../../index.js";
|
|
5
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
6
|
+
export function createJobsCommand(program) {
|
|
7
|
+
const jobs = program
|
|
8
|
+
.command("jobs")
|
|
9
|
+
.alias("j")
|
|
10
|
+
.description("Manage background jobs, automations, and triggers");
|
|
11
|
+
// ay jobs list
|
|
12
|
+
jobs
|
|
13
|
+
.command("list")
|
|
14
|
+
.alias("ls")
|
|
15
|
+
.description("List scheduled/queued jobs")
|
|
16
|
+
.option("--status <status>", "Filter: active, waiting, completed, failed, delayed")
|
|
17
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
18
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
var _a, _b, _c;
|
|
21
|
+
try {
|
|
22
|
+
const opts = { ...program.opts(), ...options };
|
|
23
|
+
spinner.start({ text: "Fetching jobs...", color: "magenta" });
|
|
24
|
+
const params = {
|
|
25
|
+
page: opts.page,
|
|
26
|
+
limit: opts.limit,
|
|
27
|
+
responseFormat: opts.responseFormat,
|
|
28
|
+
verbosity: opts.verbosity,
|
|
29
|
+
};
|
|
30
|
+
if (opts.status)
|
|
31
|
+
params.status = opts.status;
|
|
32
|
+
const res = await apiCallHandler("general", "agendajobs", "get", null, params);
|
|
33
|
+
handleResponseFormatOptions(opts, res);
|
|
34
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
35
|
+
spinner.success({ text: `Found ${total} jobs` });
|
|
36
|
+
spinner.stop();
|
|
37
|
+
if (opts.save)
|
|
38
|
+
await saveFile("jobs-list", opts, res);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
spinner.error({ text: e.message || "Failed to list jobs" });
|
|
42
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// ay jobs triggers
|
|
46
|
+
jobs
|
|
47
|
+
.command("triggers")
|
|
48
|
+
.description("List automation triggers")
|
|
49
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
50
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
var _a, _b, _c;
|
|
53
|
+
try {
|
|
54
|
+
const opts = { ...program.opts(), ...options };
|
|
55
|
+
spinner.start({ text: "Fetching triggers...", color: "magenta" });
|
|
56
|
+
const res = await apiCallHandler("automation", "triggers", "get", null, {
|
|
57
|
+
page: opts.page,
|
|
58
|
+
limit: opts.limit,
|
|
59
|
+
responseFormat: opts.responseFormat,
|
|
60
|
+
verbosity: opts.verbosity,
|
|
61
|
+
});
|
|
62
|
+
handleResponseFormatOptions(opts, res);
|
|
63
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
64
|
+
spinner.success({ text: `Found ${total} triggers` });
|
|
65
|
+
spinner.stop();
|
|
66
|
+
if (opts.save)
|
|
67
|
+
await saveFile("jobs-triggers", opts, res);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
spinner.error({ text: e.message || "Failed to list triggers" });
|
|
71
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// ay jobs automations
|
|
75
|
+
jobs
|
|
76
|
+
.command("automations")
|
|
77
|
+
.alias("auto")
|
|
78
|
+
.description("List automations")
|
|
79
|
+
.option("--active", "Show only active automations")
|
|
80
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
81
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
82
|
+
.action(async (options) => {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
try {
|
|
85
|
+
const opts = { ...program.opts(), ...options };
|
|
86
|
+
spinner.start({ text: "Fetching automations...", color: "magenta" });
|
|
87
|
+
const params = {
|
|
88
|
+
page: opts.page,
|
|
89
|
+
limit: opts.limit,
|
|
90
|
+
responseFormat: opts.responseFormat,
|
|
91
|
+
verbosity: opts.verbosity,
|
|
92
|
+
};
|
|
93
|
+
if (opts.active)
|
|
94
|
+
params.active = "true";
|
|
95
|
+
const res = await apiCallHandler("automation", "automations", "get", null, params);
|
|
96
|
+
handleResponseFormatOptions(opts, res);
|
|
97
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
98
|
+
spinner.success({ text: `Found ${total} automations` });
|
|
99
|
+
spinner.stop();
|
|
100
|
+
if (opts.save)
|
|
101
|
+
await saveFile("jobs-automations", opts, res);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
spinner.error({ text: e.message || "Failed to list automations" });
|
|
105
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// ay jobs execute <automationId>
|
|
109
|
+
jobs
|
|
110
|
+
.command("execute <automationId>")
|
|
111
|
+
.alias("run")
|
|
112
|
+
.description("Execute an automation immediately")
|
|
113
|
+
.option("--body <json>", "Optional payload as JSON")
|
|
114
|
+
.action(async (automationId, options) => {
|
|
115
|
+
try {
|
|
116
|
+
const opts = { ...program.opts(), ...options };
|
|
117
|
+
spinner.start({ text: `Executing automation ${automationId}...`, color: "magenta" });
|
|
118
|
+
let body = null;
|
|
119
|
+
if (opts.body) {
|
|
120
|
+
try {
|
|
121
|
+
body = JSON.parse(opts.body);
|
|
122
|
+
}
|
|
123
|
+
catch (_a) {
|
|
124
|
+
spinner.error({ text: "Invalid JSON in --body" });
|
|
125
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const res = await apiCallHandler("automation", `automations/${automationId}/execute`, "post", body, { responseFormat: opts.responseFormat });
|
|
129
|
+
handleResponseFormatOptions(opts, res);
|
|
130
|
+
spinner.success({ text: `Automation ${automationId} executed` });
|
|
131
|
+
spinner.stop();
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
spinner.error({ text: e.message || "Failed to execute automation" });
|
|
135
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// ay jobs notifications
|
|
139
|
+
jobs
|
|
140
|
+
.command("notifications")
|
|
141
|
+
.alias("notify")
|
|
142
|
+
.description("List recent notifications")
|
|
143
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 25)
|
|
144
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
145
|
+
.action(async (options) => {
|
|
146
|
+
var _a, _b, _c;
|
|
147
|
+
try {
|
|
148
|
+
const opts = { ...program.opts(), ...options };
|
|
149
|
+
spinner.start({ text: "Fetching notifications...", color: "magenta" });
|
|
150
|
+
const res = await apiCallHandler("general", "notifications", "get", null, {
|
|
151
|
+
page: opts.page,
|
|
152
|
+
limit: opts.limit,
|
|
153
|
+
responseFormat: opts.responseFormat,
|
|
154
|
+
verbosity: opts.verbosity,
|
|
155
|
+
});
|
|
156
|
+
handleResponseFormatOptions(opts, res);
|
|
157
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
158
|
+
spinner.success({ text: `Found ${total} notifications` });
|
|
159
|
+
spinner.stop();
|
|
160
|
+
if (opts.save)
|
|
161
|
+
await saveFile("jobs-notifications", opts, res);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
spinner.error({ text: e.message || "Failed to list notifications" });
|
|
165
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
2
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
3
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
4
|
+
import { spinner } from "../../index.js";
|
|
5
|
+
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
6
|
+
export function createPermissionsCommand(program) {
|
|
7
|
+
const perms = program
|
|
8
|
+
.command("permissions")
|
|
9
|
+
.alias("perms")
|
|
10
|
+
.description("Manage permissions, access requests, and user rights");
|
|
11
|
+
// ── PERMISSION REQUESTS ────────────────────────────────
|
|
12
|
+
const requests = perms
|
|
13
|
+
.command("requests")
|
|
14
|
+
.alias("req")
|
|
15
|
+
.description("Manage permission requests");
|
|
16
|
+
// ay permissions requests list
|
|
17
|
+
requests
|
|
18
|
+
.command("list")
|
|
19
|
+
.alias("ls")
|
|
20
|
+
.description("List permission requests")
|
|
21
|
+
.option("--status <status>", "Filter by status (pending, approved, rejected)")
|
|
22
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 25)
|
|
23
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
var _a, _b, _c;
|
|
26
|
+
try {
|
|
27
|
+
const opts = { ...program.opts(), ...options };
|
|
28
|
+
spinner.start({ text: "Fetching permission requests...", color: "magenta" });
|
|
29
|
+
const params = {
|
|
30
|
+
page: opts.page,
|
|
31
|
+
limit: opts.limit,
|
|
32
|
+
responseFormat: opts.responseFormat,
|
|
33
|
+
verbosity: opts.verbosity,
|
|
34
|
+
};
|
|
35
|
+
if (opts.status)
|
|
36
|
+
params.status = opts.status;
|
|
37
|
+
const res = await apiCallHandler("config", "permissionrequests", "get", null, params);
|
|
38
|
+
handleResponseFormatOptions(opts, res);
|
|
39
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
40
|
+
spinner.success({ text: `Found ${total} permission requests` });
|
|
41
|
+
spinner.stop();
|
|
42
|
+
if (opts.save)
|
|
43
|
+
await saveFile("permission-requests", opts, res);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
spinner.error({ text: e.message || "Failed to list permission requests" });
|
|
47
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// ay permissions requests approve <id>
|
|
51
|
+
requests
|
|
52
|
+
.command("approve <id>")
|
|
53
|
+
.description("Approve a permission request")
|
|
54
|
+
.action(async (id, options) => {
|
|
55
|
+
try {
|
|
56
|
+
const opts = { ...program.opts(), ...options };
|
|
57
|
+
spinner.start({ text: `Approving request ${id}...`, color: "magenta" });
|
|
58
|
+
const res = await apiCallHandler("config", "permissionrequests", "put", { _id: id, status: "approved" }, { responseFormat: opts.responseFormat });
|
|
59
|
+
handleResponseFormatOptions(opts, res);
|
|
60
|
+
spinner.success({ text: `Permission request ${id} approved` });
|
|
61
|
+
spinner.stop();
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
spinner.error({ text: e.message || "Failed to approve request" });
|
|
65
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// ay permissions requests reject <id>
|
|
69
|
+
requests
|
|
70
|
+
.command("reject <id>")
|
|
71
|
+
.description("Reject a permission request")
|
|
72
|
+
.option("--reason <reason>", "Reason for rejection")
|
|
73
|
+
.action(async (id, options) => {
|
|
74
|
+
try {
|
|
75
|
+
const opts = { ...program.opts(), ...options };
|
|
76
|
+
spinner.start({ text: `Rejecting request ${id}...`, color: "magenta" });
|
|
77
|
+
const body = { _id: id, status: "rejected" };
|
|
78
|
+
if (opts.reason)
|
|
79
|
+
body.reason = opts.reason;
|
|
80
|
+
const res = await apiCallHandler("config", "permissionrequests", "put", body, { responseFormat: opts.responseFormat });
|
|
81
|
+
handleResponseFormatOptions(opts, res);
|
|
82
|
+
spinner.success({ text: `Permission request ${id} rejected` });
|
|
83
|
+
spinner.stop();
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
spinner.error({ text: e.message || "Failed to reject request" });
|
|
87
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// ay permissions requests create
|
|
91
|
+
requests
|
|
92
|
+
.command("create")
|
|
93
|
+
.description("Create a new permission request")
|
|
94
|
+
.option("--body <json>", "Request definition as JSON")
|
|
95
|
+
.option("--right <right>", "Right to request (e.g. crm.contacts.edit)")
|
|
96
|
+
.option("--reason <reason>", "Reason for the request")
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
try {
|
|
99
|
+
const opts = { ...program.opts(), ...options };
|
|
100
|
+
let body = null;
|
|
101
|
+
if (opts.body) {
|
|
102
|
+
try {
|
|
103
|
+
body = JSON.parse(opts.body);
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
spinner.error({ text: "Invalid JSON in --body" });
|
|
107
|
+
process.exit(EXIT_MISUSE);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (opts.right) {
|
|
111
|
+
body = { right: opts.right };
|
|
112
|
+
if (opts.reason)
|
|
113
|
+
body.reason = opts.reason;
|
|
114
|
+
}
|
|
115
|
+
if (!body) {
|
|
116
|
+
spinner.error({ text: "Provide request via --body or --right" });
|
|
117
|
+
process.exit(EXIT_MISUSE);
|
|
118
|
+
}
|
|
119
|
+
spinner.start({ text: "Creating permission request...", color: "magenta" });
|
|
120
|
+
const res = await apiCallHandler("config", "permissionrequests", "post", body, {
|
|
121
|
+
responseFormat: opts.responseFormat,
|
|
122
|
+
});
|
|
123
|
+
handleResponseFormatOptions(opts, res);
|
|
124
|
+
spinner.success({ text: "Permission request created" });
|
|
125
|
+
spinner.stop();
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
spinner.error({ text: e.message || "Failed to create permission request" });
|
|
129
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// ── USER RIGHTS ────────────────────────────────────────
|
|
133
|
+
const rights = perms
|
|
134
|
+
.command("rights")
|
|
135
|
+
.description("View and manage user rights");
|
|
136
|
+
// ay permissions rights list
|
|
137
|
+
rights
|
|
138
|
+
.command("list")
|
|
139
|
+
.alias("ls")
|
|
140
|
+
.description("List user rights / permission assignments")
|
|
141
|
+
.option("--userId <id>", "Filter by user ID")
|
|
142
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
143
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
144
|
+
.action(async (options) => {
|
|
145
|
+
var _a, _b, _c;
|
|
146
|
+
try {
|
|
147
|
+
const opts = { ...program.opts(), ...options };
|
|
148
|
+
spinner.start({ text: "Fetching user rights...", color: "magenta" });
|
|
149
|
+
const params = {
|
|
150
|
+
page: opts.page,
|
|
151
|
+
limit: opts.limit,
|
|
152
|
+
responseFormat: opts.responseFormat,
|
|
153
|
+
verbosity: opts.verbosity,
|
|
154
|
+
};
|
|
155
|
+
if (opts.userId)
|
|
156
|
+
params.userId = opts.userId;
|
|
157
|
+
const res = await apiCallHandler("config", "userrights", "get", null, params);
|
|
158
|
+
handleResponseFormatOptions(opts, res);
|
|
159
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
160
|
+
spinner.success({ text: `Found ${total} user right assignments` });
|
|
161
|
+
spinner.stop();
|
|
162
|
+
if (opts.save)
|
|
163
|
+
await saveFile("user-rights", opts, res);
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
spinner.error({ text: e.message || "Failed to list user rights" });
|
|
167
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// ay permissions rights get <id>
|
|
171
|
+
rights
|
|
172
|
+
.command("get <id>")
|
|
173
|
+
.description("Get user right details")
|
|
174
|
+
.action(async (id, options) => {
|
|
175
|
+
try {
|
|
176
|
+
const opts = { ...program.opts(), ...options };
|
|
177
|
+
spinner.start({ text: `Fetching right ${id}...`, color: "magenta" });
|
|
178
|
+
const res = await apiCallHandler("config", `userrights/${id}`, "get", null, {
|
|
179
|
+
responseFormat: opts.responseFormat,
|
|
180
|
+
verbosity: opts.verbosity,
|
|
181
|
+
});
|
|
182
|
+
handleResponseFormatOptions(opts, res);
|
|
183
|
+
spinner.success({ text: `Right ${id} loaded` });
|
|
184
|
+
spinner.stop();
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
spinner.error({ text: e.message || "Failed to get user right" });
|
|
188
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// ── AUDIT (ENHANCED) ──────────────────────────────────
|
|
192
|
+
perms
|
|
193
|
+
.command("audit")
|
|
194
|
+
.description("Audit permission changes and access logs")
|
|
195
|
+
.addHelpText("after", `
|
|
196
|
+
Examples:
|
|
197
|
+
ay permissions audit --action delete --days 30
|
|
198
|
+
ay permissions audit --userId abc123 --collection contacts
|
|
199
|
+
ay permissions audit --action create --days 7 --save`)
|
|
200
|
+
.option("--userId <id>", "Filter by user ID")
|
|
201
|
+
.option("--action <action>", "Filter by action (create, update, delete, login)")
|
|
202
|
+
.option("--collection <name>", "Filter by collection")
|
|
203
|
+
.option("--days <number>", "Show entries from last N days", parseInt, 7)
|
|
204
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
205
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
206
|
+
.action(async (options) => {
|
|
207
|
+
var _a, _b, _c;
|
|
208
|
+
try {
|
|
209
|
+
const opts = { ...program.opts(), ...options };
|
|
210
|
+
spinner.start({ text: "Fetching audit logs...", color: "magenta" });
|
|
211
|
+
const params = {
|
|
212
|
+
page: opts.page,
|
|
213
|
+
limit: opts.limit,
|
|
214
|
+
responseFormat: opts.responseFormat,
|
|
215
|
+
verbosity: opts.verbosity,
|
|
216
|
+
};
|
|
217
|
+
if (opts.userId)
|
|
218
|
+
params.userId = opts.userId;
|
|
219
|
+
if (opts.action)
|
|
220
|
+
params.action = opts.action;
|
|
221
|
+
if (opts.collection)
|
|
222
|
+
params.collection = opts.collection;
|
|
223
|
+
if (opts.days) {
|
|
224
|
+
const fromDate = new Date();
|
|
225
|
+
fromDate.setDate(fromDate.getDate() - opts.days);
|
|
226
|
+
params.from = fromDate.toISOString();
|
|
227
|
+
}
|
|
228
|
+
const res = await apiCallHandler("general", "auditlogs", "get", null, params);
|
|
229
|
+
handleResponseFormatOptions(opts, res);
|
|
230
|
+
const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
|
|
231
|
+
spinner.success({ text: `Found ${total} audit entries (last ${opts.days} days)` });
|
|
232
|
+
spinner.stop();
|
|
233
|
+
if (opts.save)
|
|
234
|
+
await saveFile("permissions-audit", opts, res);
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
spinner.error({ text: e.message || "Failed to fetch audit logs" });
|
|
238
|
+
process.exit(EXIT_GENERAL_ERROR);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
@@ -30,6 +30,17 @@ import { enableJsonErrors } from "../api/handleAPIError.js";
|
|
|
30
30
|
import { createServicesCommand } from "./createServicesCommand.js";
|
|
31
31
|
import { createDeployCommand } from "./createDeployCommand.js";
|
|
32
32
|
import { createMonitorCommand } from "./createMonitorCommand.js";
|
|
33
|
+
import { createDeleteCommand } from "./createDeleteCommand.js";
|
|
34
|
+
import { createUpdateCommand } from "./createUpdateCommand.js";
|
|
35
|
+
import { createBatchCommand } from "./createBatchCommand.js";
|
|
36
|
+
import { createSearchCommand } from "./createSearchCommand.js";
|
|
37
|
+
import { createWebhooksCommand } from "./createWebhooksCommand.js";
|
|
38
|
+
import { createJobsCommand } from "./createJobsCommand.js";
|
|
39
|
+
import { createExportCommand } from "./createExportCommand.js";
|
|
40
|
+
import { createUsersCommand } from "./createUsersCommand.js";
|
|
41
|
+
import { createSyncCommand } from "./createSyncCommand.js";
|
|
42
|
+
import { createPermissionsCommand } from "./createPermissionsCommand.js";
|
|
43
|
+
import { createTemplateCommand } from "./createTemplateCommand.js";
|
|
33
44
|
import { localStorage } from "../helpers/localStorage.js";
|
|
34
45
|
import { login } from "../api/login.js";
|
|
35
46
|
import { loadConfig } from "../helpers/configLoader.js";
|
|
@@ -79,6 +90,17 @@ export function createProgram(program) {
|
|
|
79
90
|
createServicesCommand(program);
|
|
80
91
|
createDeployCommand(program);
|
|
81
92
|
createMonitorCommand(program);
|
|
93
|
+
createDeleteCommand(program);
|
|
94
|
+
createUpdateCommand(program);
|
|
95
|
+
createBatchCommand(program);
|
|
96
|
+
createSearchCommand(program);
|
|
97
|
+
createWebhooksCommand(program);
|
|
98
|
+
createJobsCommand(program);
|
|
99
|
+
createExportCommand(program);
|
|
100
|
+
createUsersCommand(program);
|
|
101
|
+
createSyncCommand(program);
|
|
102
|
+
createPermissionsCommand(program);
|
|
103
|
+
createTemplateCommand(program);
|
|
82
104
|
createCompletionsCommand(program);
|
|
83
105
|
createAliasCommand(program);
|
|
84
106
|
createConfigCommand(program);
|