-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
112 lines (97 loc) · 4.63 KB
/
index.ts
File metadata and controls
112 lines (97 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { AdminForthPlugin, Filters, Sorts, AdminForthDataTypes } from "adminforth";
import type { IAdminForth, IHttpServer, AdminForthResource } from "adminforth";
import type { PluginOptions } from './types.js';
import { parseHumanNumber } from './utils/parseNumber.js';
import { parseDuration } from './utils/parseDuration.js';
// Why do we need MAX_DELETE_PER_RUN?
const MAX_DELETE_PER_RUN = 500;
export default class AutoRemovePlugin extends AdminForthPlugin {
options: PluginOptions;
// I don't understand why do you need this resource config if you alredy have it below
// You can use create resource: AdminForthResourc and somewhere below just set it
// Then you will remove [this._resourceConfig.columns.find(c => c.primaryKey)!.name] and will use just resource
protected _resourceConfig!: AdminForthResource;
private timer?: NodeJS.Timeout;
constructor(options: PluginOptions) {
super(options, import.meta.url);
this.options = options;
this.shouldHaveSingleInstancePerWholeApp = () => false;
}
instanceUniqueRepresentation(pluginOptions: any) : string {
return `single`;
}
async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
super.modifyResourceConfig(adminforth, resourceConfig);
this._resourceConfig = resourceConfig;
// Start the cleanup timer
const intervalMs = parseDuration(this.options.interval || '1d');
this.timer = setInterval(() => {
this.runCleanup(adminforth).catch(console.error);
}, intervalMs);
}
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
// Check createdAtField exists and is date/datetim
const col = resourceConfig.columns.find(c => c.name === this.options.createdAtField);
// I don't like error messages look at other plugins and change to something similar
if (!col) throw new Error(`createdAtField "${this.options.createdAtField}" not found`);
if (![AdminForthDataTypes.DATE, AdminForthDataTypes.DATETIME].includes(col.type!)) {
throw new Error(`createdAtField must be date/datetime/timestamp`);
}
// Check mode-specific options
if (this.options.mode === 'count-based' && !this.options.maxItems) {
throw new Error('maxItems is required for count-based mode');
}
if (this.options.mode === 'time-based' && !this.options.maxAge) {
throw new Error('maxAge is required for time-based mode');
}
}
private async runCleanup(adminforth: IAdminForth) {
try {
if (this.options.mode === 'count-based') {
await this.cleanupByCount(adminforth);
} else {
await this.cleanupByTime(adminforth);
}
} catch (err) {
console.error('AutoRemovePlugin runCleanup error:', err);
}
}
private async cleanupByCount(adminforth: IAdminForth) {
const limit = parseHumanNumber(this.options.maxItems!);
const resource = adminforth.resource(this._resourceConfig.resourceId);
const allRecords = await resource.list([], null, null, [Sorts.ASC(this.options.createdAtField)]);
if (allRecords.length <= limit) return;
const toDelete = allRecords.slice(0, allRecords.length - limit).slice(0, this.options.maxDeletePerRun || MAX_DELETE_PER_RUN);
for (const r of toDelete) {
await resource.delete(r[this._resourceConfig.columns.find(c => c.primaryKey)!.name]);
console.log(`AutoRemovePlugin: deleted record ${r[this._resourceConfig.columns.find(c => c.primaryKey)!.name]} due to count-based limit`);
}
}
private async cleanupByTime(adminforth: IAdminForth) {
const maxAgeMs = parseDuration(this.options.maxAge!);
const threshold = Date.now() - maxAgeMs;
const resource = adminforth.resource(this._resourceConfig.resourceId);
const allRecords = await resource.list([], null, null, Sorts.ASC(this.options.createdAtField));
const toDelete = allRecords
.filter(r => new Date(r[this.options.createdAtField]).getTime() < threshold)
.slice(0, this.options.maxDeletePerRun || MAX_DELETE_PER_RUN);
for (const r of toDelete) {
await resource.delete(r[this._resourceConfig.columns.find(c => c.primaryKey)!.name]);
console.log(`AutoRemovePlugin: deleted record ${r[this._resourceConfig.columns.find(c => c.primaryKey)!.name]} due to time-based limit`);
}
}
setupEndpoints(server: IHttpServer) {
server.endpoint({
method: 'POST',
path: `/plugin/${this.pluginInstanceId}/cleanup`,
handler: async () => {
try {
await this.runCleanup(this.adminforth);
return { ok: true };
} catch (err) {
return { ok: false, error: err instanceof Error ? err.message : String(err) };
}
}
});
}
}