@onetype/framework 2.0.49 → 2.0.52
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/addons/core/commands/front/directives/run.js +1 -1
- package/addons/core/commands/front/directives/submit.js +1 -1
- package/addons/core/commands/front/functions/api.js +1 -1
- package/addons/core/database/back/addon.js +14 -0
- package/addons/core/database/back/events/addon.init.js +42 -0
- package/addons/core/database/back/events/addon.item.init.js +24 -0
- package/addons/core/database/back/functions/item/create.js +92 -0
- package/addons/core/database/back/functions/item/delete.js +34 -0
- package/addons/core/database/back/functions/item/save.js +13 -0
- package/addons/core/database/back/functions/item/update.js +90 -0
- package/addons/core/database/back/functions/items/builder.js +160 -0
- package/addons/core/database/back/functions/items/filter.js +32 -0
- package/addons/core/database/back/functions/items/filters.js +7 -0
- package/addons/core/database/back/functions/items/find.js +28 -0
- package/addons/core/database/back/functions/items/methods/aggregate.js +15 -0
- package/addons/core/database/back/functions/items/methods/count.js +12 -0
- package/addons/core/database/back/functions/items/methods/exists.js +8 -0
- package/addons/core/database/back/functions/items/methods/many.js +13 -0
- package/addons/core/database/back/functions/items/methods/one.js +8 -0
- package/addons/core/database/back/functions/items/methods/plain.js +29 -0
- package/addons/core/database/back/functions/items/methods/query.js +33 -0
- package/addons/core/database/back/functions/items/methods.js +142 -0
- package/addons/core/database/back/functions/items/transform/join.js +63 -0
- package/addons/core/database/back/functions/items/transform/translate.js +57 -0
- package/addons/core/database/back/functions/items/validation.js +125 -0
- package/addons/core/database/back/item/catch/add.js +1 -1
- package/addons/core/database/back/items/commands/create.js +54 -0
- package/addons/core/database/back/items/commands/delete.js +62 -0
- package/addons/core/database/back/items/commands/find.js +118 -0
- package/addons/core/database/back/items/commands/update.js +70 -0
- package/addons/core/database/back/load.js +27 -26
- package/addons/core/database/front/events/addon.init.js +42 -0
- package/addons/core/database/front/functions/create.js +11 -0
- package/addons/core/database/front/functions/delete.js +10 -0
- package/addons/core/database/front/functions/find.js +96 -142
- package/addons/core/database/front/functions/update.js +12 -0
- package/addons/float/popup/css/popup.css +141 -18
- package/addons/float/popup/js/addon.js +5 -0
- package/addons/float/popup/js/functions/confirm.js +100 -0
- package/addons/float/popup/js/functions/toast.js +1 -1
- package/addons/float/popup/js/items/directives/tooltip.js +5 -1
- package/addons/render/directives/front/addon.js +5 -0
- package/addons/render/directives/front/functions/process/data.js +19 -1
- package/addons/render/directives/front/functions/process/locale.js +46 -0
- package/addons/render/directives/front/functions/process.js +4 -1
- package/addons/render/directives/front/items/self/660-form.js +74 -163
- package/addons/render/directives/front/items/self/750-html.js +1 -1
- package/addons/render/elements/front/items/self/form/button/button.css +9 -0
- package/addons/render/elements/front/items/self/form/button/button.js +1 -1
- package/addons/render/elements/front/items/self/form/color/color.css +1 -1
- package/addons/render/elements/front/items/self/form/color/color.js +25 -10
- package/addons/render/elements/front/items/self/form/editor-markdown/editor-markdown.css +410 -0
- package/addons/render/elements/front/items/self/form/editor-markdown/editor-markdown.js +191 -0
- package/addons/render/elements/front/items/self/form/field/field.css +18 -4
- package/addons/render/elements/front/items/self/form/field/field.js +6 -1
- package/addons/render/elements/front/items/self/form/section/section.js +3 -1
- package/addons/render/elements/front/items/self/navigation/tabs/tabs.css +8 -3
- package/addons/render/pages/core/addon.js +1 -1
- package/addons/render/pages/front/items/directives/change.js +1 -1
- package/addons/render/pages/front/styles/page.css +0 -7
- package/addons/render/transforms/js/functions/data.js +21 -2
- package/lib/src/classes/addon/class.js +0 -16
- package/lib/src/classes/addon/classes/item/class.js +0 -2
- package/lib/src/classes/addon/classes/item/mixins/get.js +0 -1
- package/lib/src/classes/addon/classes/render/mixins/compile.js +3 -2
- package/lib/src/classes/addon/mixins/items.js +2 -0
- package/lib/src/mixins/addons.js +2 -0
- package/lib/src/mixins/data.js +10 -9
- package/lib/src/mixins/emitter.js +5 -1
- package/lib/src/mixins/form.js +4 -0
- package/lib/src/mixins/function.js +6 -1
- package/lib/src/mixins/language.js +55 -0
- package/lib/src/mixins/locale.js +49 -0
- package/lib/src/onetype.js +6 -11
- package/package.json +1 -1
- package/addons/core/database/back/events/addon.add.js +0 -18
- package/addons/core/database/back/events/middleware/addon.items.find.js +0 -24
- package/addons/core/database/back/events/middleware/item.crud.create.js +0 -24
- package/addons/core/database/back/events/middleware/item.crud.delete.js +0 -24
- package/addons/core/database/back/events/middleware/item.crud.update.js +0 -24
- package/addons/core/database/back/functions/create.js +0 -6
- package/addons/core/database/back/functions/delete.js +0 -6
- package/addons/core/database/back/functions/find/builder.js +0 -160
- package/addons/core/database/back/functions/find/count.js +0 -12
- package/addons/core/database/back/functions/find/filter.js +0 -37
- package/addons/core/database/back/functions/find/filters.js +0 -7
- package/addons/core/database/back/functions/find/many.js +0 -93
- package/addons/core/database/back/functions/find/methods.js +0 -235
- package/addons/core/database/back/functions/find/plain.js +0 -25
- package/addons/core/database/back/functions/find/validation.js +0 -214
- package/addons/core/database/back/functions/find.js +0 -25
- package/addons/core/database/back/functions/update.js +0 -6
- package/addons/core/database/back/item/functions/create.js +0 -94
- package/addons/core/database/back/item/functions/delete.js +0 -25
- package/addons/core/database/back/item/functions/find.js +0 -19
- package/addons/core/database/back/item/functions/save.js +0 -15
- package/addons/core/database/back/item/functions/transaction.js +0 -17
- package/addons/core/database/back/item/functions/update.js +0 -76
- package/addons/core/database/back/items/commands/expose.js +0 -321
- package/addons/core/database/front/events/addon.add.js +0 -13
- package/lib/src/classes/addon/classes/item/mixins/crud.js +0 -28
- package/lib/src/classes/addon/mixins/find.js +0 -12
- package/lib/src/classes/addon/mixins/table.js +0 -35
- package/lib/src/mixins/dependencies.js +0 -104
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import database from '#database/addon.js';
|
|
2
|
+
|
|
3
|
+
database.Fn('items.methods.query', async function(query)
|
|
4
|
+
{
|
|
5
|
+
const builder = database.Fn('items.builder');
|
|
6
|
+
const knexQuery = query.knex(query.table.name);
|
|
7
|
+
|
|
8
|
+
builder.applySelect(knexQuery, query.select, query.distinct);
|
|
9
|
+
builder.applyFilters(knexQuery, query.filters);
|
|
10
|
+
builder.applySort(knexQuery, query.sort);
|
|
11
|
+
builder.applyPagination(knexQuery, query.limit, query.page);
|
|
12
|
+
|
|
13
|
+
const result = await knexQuery;
|
|
14
|
+
|
|
15
|
+
const records = result.map((record) =>
|
|
16
|
+
{
|
|
17
|
+
const data = {};
|
|
18
|
+
|
|
19
|
+
Object.entries(record).forEach(([key, value]) =>
|
|
20
|
+
{
|
|
21
|
+
if(value instanceof Date)
|
|
22
|
+
{
|
|
23
|
+
value = value.toISOString();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
data[key] = value;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return data;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return database.Fn('items.transform.translate', records, query);
|
|
33
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import onetype from '#framework/load.js';
|
|
2
|
+
import database from '#database/addon.js';
|
|
3
|
+
|
|
4
|
+
database.Fn('items.methods', function(query, context = null, groupId = 'default')
|
|
5
|
+
{
|
|
6
|
+
const validation = database.Fn('items.validation');
|
|
7
|
+
const methods = {};
|
|
8
|
+
|
|
9
|
+
methods.limit = limit =>
|
|
10
|
+
{
|
|
11
|
+
validation.limit(limit);
|
|
12
|
+
query.limit = limit;
|
|
13
|
+
return methods;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
methods.page = page =>
|
|
17
|
+
{
|
|
18
|
+
validation.page(page);
|
|
19
|
+
query.page = page;
|
|
20
|
+
return methods;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
methods.sort = (field, direction = 'asc') =>
|
|
24
|
+
{
|
|
25
|
+
validation.field(field);
|
|
26
|
+
direction = validation.direction(direction);
|
|
27
|
+
|
|
28
|
+
query.sort = { field, direction };
|
|
29
|
+
return methods;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
methods.select = fields =>
|
|
33
|
+
{
|
|
34
|
+
fields = Array.isArray(fields) ? fields : [fields];
|
|
35
|
+
validation.fields(fields);
|
|
36
|
+
|
|
37
|
+
query.select = fields;
|
|
38
|
+
return methods;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
methods.distinct = (value = true) =>
|
|
42
|
+
{
|
|
43
|
+
query.distinct = Boolean(value);
|
|
44
|
+
return methods;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
methods.filter = (field, value, operator = 'EQUALS') =>
|
|
48
|
+
{
|
|
49
|
+
return database.Fn('items.filter', query, field, value, operator, 'AND', groupId, methods);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
methods.orFilter = (field, value, operator = 'EQUALS') =>
|
|
53
|
+
{
|
|
54
|
+
return database.Fn('items.filter', query, field, value, operator, 'OR', groupId, methods);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
methods.group = (type = 'AND') =>
|
|
58
|
+
{
|
|
59
|
+
const TID = onetype.GenerateTID();
|
|
60
|
+
|
|
61
|
+
if(!query.filters)
|
|
62
|
+
{
|
|
63
|
+
query.filters = [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
query.filters.push({ groupStart: true, group: TID, type });
|
|
67
|
+
|
|
68
|
+
const groupMethods = database.Fn('items.methods', query, methods, TID);
|
|
69
|
+
groupMethods.end = () => context || methods;
|
|
70
|
+
|
|
71
|
+
return groupMethods;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
methods.join = (addon, field, output = null) =>
|
|
75
|
+
{
|
|
76
|
+
const config = query.addon.FieldGet(field);
|
|
77
|
+
|
|
78
|
+
if(!config)
|
|
79
|
+
{
|
|
80
|
+
throw new Error(`Field '${field}' not found in addon`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const parsed = onetype.DataParseConfig(config.define);
|
|
84
|
+
const many = parsed.type.includes('array');
|
|
85
|
+
|
|
86
|
+
query.joins.push({ addon, field, output: output || field, many });
|
|
87
|
+
|
|
88
|
+
return methods;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
methods.many = async (set = false) =>
|
|
92
|
+
{
|
|
93
|
+
return database.Fn('items.methods.many', query, set);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
methods.plain = async () =>
|
|
97
|
+
{
|
|
98
|
+
return database.Fn('items.methods.plain', query);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
methods.count = async () =>
|
|
102
|
+
{
|
|
103
|
+
return database.Fn('items.methods.count', query);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
methods.one = async (set = false) =>
|
|
107
|
+
{
|
|
108
|
+
return database.Fn('items.methods.one', query, set);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
methods.exists = async () =>
|
|
112
|
+
{
|
|
113
|
+
return database.Fn('items.methods.exists', query);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
methods.sum = async (field) =>
|
|
117
|
+
{
|
|
118
|
+
return database.Fn('items.methods.aggregate', query, 'sum', field);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
methods.avg = async (field) =>
|
|
122
|
+
{
|
|
123
|
+
return database.Fn('items.methods.aggregate', query, 'avg', field);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
methods.min = async (field) =>
|
|
127
|
+
{
|
|
128
|
+
return database.Fn('items.methods.aggregate', query, 'min', field);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
methods.max = async (field) =>
|
|
132
|
+
{
|
|
133
|
+
return database.Fn('items.methods.aggregate', query, 'max', field);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if(context)
|
|
137
|
+
{
|
|
138
|
+
methods.end = () => context;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return methods;
|
|
142
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import onetype from '#framework/load.js';
|
|
2
|
+
import database from '#database/addon.js';
|
|
3
|
+
|
|
4
|
+
database.Fn('items.transform.join', async function(records, joins)
|
|
5
|
+
{
|
|
6
|
+
if(!joins.length || !records.length)
|
|
7
|
+
{
|
|
8
|
+
return records;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
for(const join of joins)
|
|
12
|
+
{
|
|
13
|
+
const addon = onetype.AddonGet(join.addon);
|
|
14
|
+
|
|
15
|
+
if(!addon)
|
|
16
|
+
{
|
|
17
|
+
throw new Error(`Join addon '${join.addon}' not found`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ids = [];
|
|
21
|
+
|
|
22
|
+
records.forEach(record =>
|
|
23
|
+
{
|
|
24
|
+
const value = record[join.field];
|
|
25
|
+
|
|
26
|
+
if(join.many && Array.isArray(value))
|
|
27
|
+
{
|
|
28
|
+
value.forEach(id => { if(id && !ids.includes(id)) ids.push(id); });
|
|
29
|
+
}
|
|
30
|
+
else if(value && !ids.includes(value))
|
|
31
|
+
{
|
|
32
|
+
ids.push(value);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if(!ids.length)
|
|
37
|
+
{
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const joined = await addon.Find().filter('id', ids, 'IN').limit(ids.length).many();
|
|
42
|
+
const map = {};
|
|
43
|
+
|
|
44
|
+
const fields = Object.keys(addon.Fields().data);
|
|
45
|
+
joined.forEach(item => { map[String(item.Get('id'))] = item.Get(fields); });
|
|
46
|
+
|
|
47
|
+
records.forEach(record =>
|
|
48
|
+
{
|
|
49
|
+
const value = record[join.field];
|
|
50
|
+
|
|
51
|
+
if(join.many && Array.isArray(value))
|
|
52
|
+
{
|
|
53
|
+
record[join.output] = value.map(id => map[String(id)]).filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
else if(value)
|
|
56
|
+
{
|
|
57
|
+
record[join.output] = map[String(value)] || null;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return records;
|
|
63
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import database from '#database/addon.js';
|
|
2
|
+
|
|
3
|
+
database.Fn('items.transform.translate', async function(records, query)
|
|
4
|
+
{
|
|
5
|
+
const translations = query.addon.database.translations;
|
|
6
|
+
|
|
7
|
+
if(!translations || !records.length)
|
|
8
|
+
{
|
|
9
|
+
return records;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ids = records.map(record => String(record.id)).filter(Boolean);
|
|
13
|
+
|
|
14
|
+
if(!ids.length)
|
|
15
|
+
{
|
|
16
|
+
return records;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const rows = await query.knex('translations')
|
|
20
|
+
.where({entity: query.addon.name, language: query.translation})
|
|
21
|
+
.whereIn('entity_id', ids);
|
|
22
|
+
|
|
23
|
+
if(!rows.length)
|
|
24
|
+
{
|
|
25
|
+
return records;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const map = {};
|
|
29
|
+
|
|
30
|
+
rows.forEach(row =>
|
|
31
|
+
{
|
|
32
|
+
if(!map[row.entity_id])
|
|
33
|
+
{
|
|
34
|
+
map[row.entity_id] = {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
map[row.entity_id][row.field] = row.value;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
records.forEach(record =>
|
|
41
|
+
{
|
|
42
|
+
const translated = map[String(record.id)];
|
|
43
|
+
|
|
44
|
+
if(translated)
|
|
45
|
+
{
|
|
46
|
+
for(const field of translations)
|
|
47
|
+
{
|
|
48
|
+
if(translated[field] !== undefined)
|
|
49
|
+
{
|
|
50
|
+
record[field] = translated[field];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return records;
|
|
57
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import database from '#database/addon.js';
|
|
2
|
+
|
|
3
|
+
database.Fn('items.validation', function()
|
|
4
|
+
{
|
|
5
|
+
const validation = {};
|
|
6
|
+
|
|
7
|
+
validation.field = field =>
|
|
8
|
+
{
|
|
9
|
+
if(field === undefined || field === null)
|
|
10
|
+
{
|
|
11
|
+
throw new Error('Field name cannot be null or undefined');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if(typeof field !== 'string')
|
|
15
|
+
{
|
|
16
|
+
throw new Error(`Field name must be a string, received: ${typeof field}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if(!(/^[a-zA-Z][a-zA-Z0-9_\.]{0,63}$/.test(field)))
|
|
20
|
+
{
|
|
21
|
+
throw new Error(`Invalid field name format: '${field}'.`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
validation.fields = fields =>
|
|
28
|
+
{
|
|
29
|
+
if(!Array.isArray(fields))
|
|
30
|
+
{
|
|
31
|
+
throw new Error(`Fields must be an array, received: ${typeof fields}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if(fields.length === 0)
|
|
35
|
+
{
|
|
36
|
+
throw new Error('Fields array cannot be empty');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for(let i = 0; i < fields.length; i++)
|
|
40
|
+
{
|
|
41
|
+
validation.field(fields[i]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
validation.limit = limit =>
|
|
48
|
+
{
|
|
49
|
+
if(typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0)
|
|
50
|
+
{
|
|
51
|
+
throw new Error(`Limit must be a positive integer, received: ${limit}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
validation.page = page =>
|
|
58
|
+
{
|
|
59
|
+
if(typeof page !== 'number' || !Number.isInteger(page) || page < 1)
|
|
60
|
+
{
|
|
61
|
+
throw new Error(`Page must be an integer >= 1, received: ${page}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
validation.direction = direction =>
|
|
68
|
+
{
|
|
69
|
+
if(typeof direction !== 'string')
|
|
70
|
+
{
|
|
71
|
+
throw new Error(`Sort direction must be a string, received: ${typeof direction}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
direction = direction.toLowerCase();
|
|
75
|
+
|
|
76
|
+
if(!['asc', 'desc'].includes(direction))
|
|
77
|
+
{
|
|
78
|
+
throw new Error(`Invalid sort direction: '${direction}'.`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return direction;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
validation.operator = (operator, operators) =>
|
|
85
|
+
{
|
|
86
|
+
if(!operators.includes(operator.toUpperCase()))
|
|
87
|
+
{
|
|
88
|
+
throw new Error(`Invalid operator: '${operator}'.`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return true;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
validation.value = value =>
|
|
95
|
+
{
|
|
96
|
+
if(value !== null && !['number', 'string', 'boolean'].includes(typeof value))
|
|
97
|
+
{
|
|
98
|
+
throw new Error(`Value must be a string, number, or boolean. Received: ${typeof value}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return true;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
validation.between = value =>
|
|
105
|
+
{
|
|
106
|
+
if(!Array.isArray(value) || value.length !== 2)
|
|
107
|
+
{
|
|
108
|
+
throw new Error('BETWEEN requires an array of exactly 2 elements.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
validation.inList = value =>
|
|
115
|
+
{
|
|
116
|
+
if(!Array.isArray(value) || value.length === 0)
|
|
117
|
+
{
|
|
118
|
+
throw new Error('IN requires a non-empty array.');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return validation;
|
|
125
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import onetype from '#framework/load.js';
|
|
2
|
+
|
|
3
|
+
onetype.AddonReady('commands', (commands) =>
|
|
4
|
+
{
|
|
5
|
+
commands.Item({
|
|
6
|
+
id: 'database:create',
|
|
7
|
+
exposed: true,
|
|
8
|
+
method: 'POST',
|
|
9
|
+
endpoint: '/api/database/create',
|
|
10
|
+
in: {
|
|
11
|
+
addon: ['string', null, true],
|
|
12
|
+
data: ['object', null, true],
|
|
13
|
+
translation: ['string']
|
|
14
|
+
},
|
|
15
|
+
out: {
|
|
16
|
+
item: ['object']
|
|
17
|
+
},
|
|
18
|
+
callback: async function(properties, resolve)
|
|
19
|
+
{
|
|
20
|
+
const addon = onetype.AddonGet(properties.addon);
|
|
21
|
+
|
|
22
|
+
if(!addon)
|
|
23
|
+
{
|
|
24
|
+
return resolve(null, 'Addon not found.', 404);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const expose = addon.database?.expose;
|
|
28
|
+
|
|
29
|
+
if(!expose)
|
|
30
|
+
{
|
|
31
|
+
return resolve(null, 'Addon is not exposed.', 403);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if(!expose.create)
|
|
35
|
+
{
|
|
36
|
+
return resolve(null, 'Create is not allowed.', 403);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const item = addon.Item(properties.data);
|
|
40
|
+
|
|
41
|
+
const allowed = expose.create.call({http: this.http, properties, item});
|
|
42
|
+
|
|
43
|
+
if(allowed !== true)
|
|
44
|
+
{
|
|
45
|
+
return resolve(null, typeof allowed === 'string' ? allowed : 'Create not allowed.', 400);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const created = await item.Create({translation: properties.translation});
|
|
49
|
+
const fields = expose.select || Object.keys(addon.Fields().data);
|
|
50
|
+
|
|
51
|
+
resolve({item: created.Get(fields)});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import onetype from '#framework/load.js';
|
|
2
|
+
|
|
3
|
+
onetype.AddonReady('commands', (commands) =>
|
|
4
|
+
{
|
|
5
|
+
commands.Item({
|
|
6
|
+
id: 'database:delete',
|
|
7
|
+
exposed: true,
|
|
8
|
+
method: 'POST',
|
|
9
|
+
endpoint: '/api/database/delete',
|
|
10
|
+
in: {
|
|
11
|
+
addon: ['string', null, true],
|
|
12
|
+
id: ['string', null, true]
|
|
13
|
+
},
|
|
14
|
+
out: {
|
|
15
|
+
success: ['boolean']
|
|
16
|
+
},
|
|
17
|
+
callback: async function(properties, resolve)
|
|
18
|
+
{
|
|
19
|
+
const addon = onetype.AddonGet(properties.addon);
|
|
20
|
+
|
|
21
|
+
if(!addon)
|
|
22
|
+
{
|
|
23
|
+
return resolve(null, 'Addon not found.', 404);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const expose = addon.database?.expose;
|
|
27
|
+
|
|
28
|
+
if(!expose)
|
|
29
|
+
{
|
|
30
|
+
return resolve(null, 'Addon is not exposed.', 403);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if(!expose.delete)
|
|
34
|
+
{
|
|
35
|
+
return resolve(null, 'Delete is not allowed.', 403);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if(!properties.data?.id || (typeof properties.data.id !== 'string' && typeof properties.data.id !== 'number'))
|
|
39
|
+
{
|
|
40
|
+
return resolve(null, 'Invalid or missing id.', 400);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const item = await addon.Find().filter('id', properties.data.id).one(true);
|
|
44
|
+
|
|
45
|
+
if(!item)
|
|
46
|
+
{
|
|
47
|
+
return resolve(null, 'Item not found.', 404);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const allowed = expose.delete.call({http: this.http, properties, item});
|
|
51
|
+
|
|
52
|
+
if(allowed !== true)
|
|
53
|
+
{
|
|
54
|
+
return resolve(null, typeof allowed === 'string' ? allowed : 'Delete not allowed.', 400);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await item.Delete();
|
|
58
|
+
|
|
59
|
+
resolve({success: true});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import onetype from '#framework/load.js';
|
|
2
|
+
|
|
3
|
+
onetype.AddonReady('commands', (commands) =>
|
|
4
|
+
{
|
|
5
|
+
commands.Item({
|
|
6
|
+
id: 'database:find',
|
|
7
|
+
exposed: true,
|
|
8
|
+
method: 'POST',
|
|
9
|
+
endpoint: '/api/database/find',
|
|
10
|
+
in: {
|
|
11
|
+
addon: ['string', null, true],
|
|
12
|
+
filters: {
|
|
13
|
+
type: 'array',
|
|
14
|
+
each: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
config: 'filter'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
sort_field: ['string'],
|
|
20
|
+
sort_direction: ['string', 'asc'],
|
|
21
|
+
select: {
|
|
22
|
+
type: 'array',
|
|
23
|
+
each: {
|
|
24
|
+
type: 'string'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
page: ['number', 1],
|
|
28
|
+
limit: ['number', 50],
|
|
29
|
+
translation: ['string']
|
|
30
|
+
},
|
|
31
|
+
out: {
|
|
32
|
+
items: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
each: {
|
|
35
|
+
type: 'object'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
total: ['number'],
|
|
39
|
+
page: ['number'],
|
|
40
|
+
limit: ['number']
|
|
41
|
+
},
|
|
42
|
+
callback: async function(properties, resolve)
|
|
43
|
+
{
|
|
44
|
+
const addon = onetype.AddonGet(properties.addon);
|
|
45
|
+
|
|
46
|
+
if(!addon)
|
|
47
|
+
{
|
|
48
|
+
return resolve(null, 'Addon not found.', 404);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const expose = addon.database?.expose;
|
|
52
|
+
|
|
53
|
+
if(!expose)
|
|
54
|
+
{
|
|
55
|
+
return resolve(null, 'Addon is not exposed.', 403);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let query = addon.Find({translation: properties.translation});
|
|
59
|
+
|
|
60
|
+
if(properties.filters)
|
|
61
|
+
{
|
|
62
|
+
for(const filter of properties.filters)
|
|
63
|
+
{
|
|
64
|
+
if(!expose.filter.includes(filter.field))
|
|
65
|
+
{
|
|
66
|
+
return resolve(null, 'Filter field "' + filter.field + '" is not allowed.', 400);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
query = query.filter(filter.field, filter.value, filter.operator || 'EQUALS');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if(properties.sort_field)
|
|
74
|
+
{
|
|
75
|
+
if(!expose.sort.includes(properties.sort_field))
|
|
76
|
+
{
|
|
77
|
+
return resolve(null, 'Sort field "' + properties.sort_field + '" is not allowed.', 400);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
query = query.sort(properties.sort_field, properties.sort_direction || 'asc');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if(expose.find)
|
|
84
|
+
{
|
|
85
|
+
const allowed = expose.find.call({http: this.http, properties}, query);
|
|
86
|
+
|
|
87
|
+
if(allowed !== true)
|
|
88
|
+
{
|
|
89
|
+
return resolve(null, typeof allowed === 'string' ? allowed : 'Find not allowed.', 400);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const limit = Math.min(properties.limit || 50, 500);
|
|
94
|
+
const page = properties.page || 1;
|
|
95
|
+
|
|
96
|
+
const result = await query.page(page).limit(limit).plain();
|
|
97
|
+
|
|
98
|
+
if(expose.select)
|
|
99
|
+
{
|
|
100
|
+
const allowed = properties.select?.length ? properties.select.filter(field => expose.select.includes(field)) : expose.select;
|
|
101
|
+
|
|
102
|
+
result.items = result.items.map(item =>
|
|
103
|
+
{
|
|
104
|
+
const data = {};
|
|
105
|
+
|
|
106
|
+
for(const field of allowed)
|
|
107
|
+
{
|
|
108
|
+
data[field] = item[field];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return data;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
resolve(result);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|