proto.io 0.0.227 → 0.0.229
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 +1017 -0
- package/dist/adapters/file/aliyun-oss.d.mts +26 -0
- package/dist/adapters/file/aliyun-oss.d.mts.map +1 -0
- package/dist/adapters/file/aliyun-oss.d.ts +3 -3
- package/dist/adapters/file/database.d.mts +23 -0
- package/dist/adapters/file/database.d.mts.map +1 -0
- package/dist/adapters/file/database.d.ts +2 -2
- package/dist/adapters/file/database.js +1 -1
- package/dist/adapters/file/database.mjs +1 -1
- package/dist/adapters/file/filesystem.d.mts +25 -0
- package/dist/adapters/file/filesystem.d.mts.map +1 -0
- package/dist/adapters/file/filesystem.d.ts +3 -3
- package/dist/adapters/file/google-cloud-storage.d.mts +29 -0
- package/dist/adapters/file/google-cloud-storage.d.mts.map +1 -0
- package/dist/adapters/file/google-cloud-storage.d.ts +3 -3
- package/dist/adapters/storage/postgres.d.mts +299 -0
- package/dist/adapters/storage/postgres.d.mts.map +1 -0
- package/dist/adapters/storage/postgres.d.ts +5 -1
- package/dist/adapters/storage/postgres.d.ts.map +1 -1
- package/dist/adapters/storage/postgres.js +182 -74
- package/dist/adapters/storage/postgres.js.map +1 -1
- package/dist/adapters/storage/postgres.mjs +182 -74
- package/dist/adapters/storage/postgres.mjs.map +1 -1
- package/dist/client.d.mts +16 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/client.mjs +2 -2
- package/dist/index.d.mts +151 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +68 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +69 -26
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{base-DSo02iAX.d.ts → base-Bhrj5Pq1.d.ts} +2 -2
- package/dist/internals/{base-DSo02iAX.d.ts.map → base-Bhrj5Pq1.d.ts.map} +1 -1
- package/dist/internals/base-CiZHXD0o.d.mts +27 -0
- package/dist/internals/base-CiZHXD0o.d.mts.map +1 -0
- package/dist/internals/chunk-Cp2QN7ug.d.mts +17 -0
- package/dist/internals/chunk-Cp2QN7ug.d.mts.map +1 -0
- package/dist/internals/{chunk-BhwfdCdq.d.ts → chunk-o7lWIP-f.d.ts} +3 -3
- package/dist/internals/{chunk-BhwfdCdq.d.ts.map → chunk-o7lWIP-f.d.ts.map} +1 -1
- package/dist/internals/{index-vOFh8pVc.js → index-B0TO6h9r.js} +8 -1
- package/dist/internals/index-B0TO6h9r.js.map +1 -0
- package/dist/internals/{index-Cj45GkKv.d.ts → index-B710pfTH.d.ts} +2 -2
- package/dist/internals/{index-Cj45GkKv.d.ts.map → index-B710pfTH.d.ts.map} +1 -1
- package/dist/internals/{index-BWZIV3_T.mjs → index-DG2-4tQ1.mjs} +8 -1
- package/dist/internals/index-DG2-4tQ1.mjs.map +1 -0
- package/dist/internals/index-DwjvuRyl.d.mts +92 -0
- package/dist/internals/index-DwjvuRyl.d.mts.map +1 -0
- package/dist/internals/index-OwgXw07h.d.mts +2107 -0
- package/dist/internals/index-OwgXw07h.d.mts.map +1 -0
- package/dist/internals/{index-1ZK5N4yb.d.ts → index-OwgXw07h.d.ts} +49 -7
- package/dist/internals/index-OwgXw07h.d.ts.map +1 -0
- package/dist/internals/{validator-Bc1jRJfA.js → validator-CFlx3oyq.js} +33 -1
- package/dist/internals/validator-CFlx3oyq.js.map +1 -0
- package/dist/internals/{validator-Boj1PUjM.mjs → validator-DubDY921.mjs} +32 -2
- package/dist/internals/validator-DubDY921.mjs.map +1 -0
- package/package.json +7 -19
- package/dist/internals/index-1ZK5N4yb.d.ts.map +0 -1
- package/dist/internals/index-BWZIV3_T.mjs.map +0 -1
- package/dist/internals/index-vOFh8pVc.js.map +0 -1
- package/dist/internals/validator-Bc1jRJfA.js.map +0 -1
- package/dist/internals/validator-Boj1PUjM.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { Server } from '@o2ter/server-js';
|
|
3
|
-
import { Q as QueryValidator, a as QueryExpression, r as resolveColumn,
|
|
3
|
+
import { Q as QueryValidator, a as QueryExpression, b as QueryAccumulator, r as resolveColumn, c as resolveDataType, d as QuerySelector } from './internals/validator-DubDY921.mjs';
|
|
4
4
|
import { P as PVK } from './internals/private-CNw40LZ7.mjs';
|
|
5
5
|
import { prototypes, asyncStream, isBinaryData, base64ToBuffer } from '@o2ter/utils-js';
|
|
6
|
-
import { L as LiveQuerySubscription, T as TQuery, d as deserialize, s as serialize, a as TUser, P as ProtoType, _ as _logLevels } from './internals/index-
|
|
7
|
-
export { b as ProtoClient, c as classExtends } from './internals/index-
|
|
6
|
+
import { L as LiveQuerySubscription, T as TQuery, d as deserialize, s as serialize, a as TUser, P as ProtoType, _ as _logLevels } from './internals/index-DG2-4tQ1.mjs';
|
|
7
|
+
export { b as ProtoClient, c as classExtends } from './internals/index-DG2-4tQ1.mjs';
|
|
8
8
|
import { i as isPointer, a as isRelation, T as TObject, d as defaultObjectKeyTypes, b as isShape, c as isPrimitive } from './internals/index-CSOoWRr1.mjs';
|
|
9
9
|
import jwt from 'jsonwebtoken';
|
|
10
10
|
import { Blob } from 'node:buffer';
|
|
@@ -113,6 +113,16 @@ const dispatcher = (proto, options) => {
|
|
|
113
113
|
throw Error('No permission');
|
|
114
114
|
return proto.storage.find(decoded);
|
|
115
115
|
},
|
|
116
|
+
async groupFind(query, accumulators) {
|
|
117
|
+
QueryValidator.recursiveCheck(query);
|
|
118
|
+
const _validator = await validator();
|
|
119
|
+
const decoded = _validator.decodeQuery(normalize(query), 'read');
|
|
120
|
+
const isGet = _validator.isGetMethod(decoded.filter);
|
|
121
|
+
if (!_validator.validateCLPs(query.className, isGet ? 'get' : 'find'))
|
|
122
|
+
throw Error('No permission');
|
|
123
|
+
const acc = _.mapValues(accumulators, x => QueryAccumulator.decode(x).simplify());
|
|
124
|
+
return proto.storage.groupFind(decoded, acc);
|
|
125
|
+
},
|
|
116
126
|
async nonrefs(query) {
|
|
117
127
|
QueryValidator.recursiveCheck(query);
|
|
118
128
|
const _validator = await validator();
|
|
@@ -309,6 +319,9 @@ class _ProtoQuery extends TQuery {
|
|
|
309
319
|
yield self._objectMethods(object);
|
|
310
320
|
});
|
|
311
321
|
}
|
|
322
|
+
groupFind(accumulators, options) {
|
|
323
|
+
return this._dispatcher(options).groupFind(this._queryOptions, accumulators);
|
|
324
|
+
}
|
|
312
325
|
nonrefs(options) {
|
|
313
326
|
const self = this;
|
|
314
327
|
return asyncStream(async function* () {
|
|
@@ -523,6 +536,8 @@ const defaultSchema = {
|
|
|
523
536
|
'User': {
|
|
524
537
|
fields: {
|
|
525
538
|
password: 'object',
|
|
539
|
+
password_history: 'array',
|
|
540
|
+
password_changed_at: 'date',
|
|
526
541
|
},
|
|
527
542
|
classLevelPermissions: {
|
|
528
543
|
find: [],
|
|
@@ -532,7 +547,7 @@ const defaultSchema = {
|
|
|
532
547
|
fieldLevelPermissions: {
|
|
533
548
|
_expired_at: { create: [], update: [] },
|
|
534
549
|
},
|
|
535
|
-
secureFields: ['password'],
|
|
550
|
+
secureFields: ['password', 'password_history', 'password_changed_at'],
|
|
536
551
|
},
|
|
537
552
|
'Role': {
|
|
538
553
|
fields: {
|
|
@@ -701,7 +716,7 @@ const passwordHash = async (alg, password, options) => {
|
|
|
701
716
|
...options
|
|
702
717
|
};
|
|
703
718
|
};
|
|
704
|
-
const
|
|
719
|
+
const verifyPassword = async (alg, password, options) => {
|
|
705
720
|
if (!_.isString(options.salt))
|
|
706
721
|
return false;
|
|
707
722
|
if (!_.isString(options.derivedKey))
|
|
@@ -898,7 +913,7 @@ class ProtoInternal {
|
|
|
898
913
|
}
|
|
899
914
|
return callback(proxy(payload ?? proto));
|
|
900
915
|
}
|
|
901
|
-
async
|
|
916
|
+
async verifyPassword(proto, user, password, options) {
|
|
902
917
|
if (!user.id)
|
|
903
918
|
throw Error('Invalid user object');
|
|
904
919
|
const _user = await proto.InsecureQuery('User')
|
|
@@ -906,20 +921,46 @@ class ProtoInternal {
|
|
|
906
921
|
.includes('_id', 'password')
|
|
907
922
|
.first(options);
|
|
908
923
|
const { alg, ...opts } = _user?.get('password') ?? {};
|
|
909
|
-
return
|
|
924
|
+
return verifyPassword(alg, password, opts);
|
|
910
925
|
}
|
|
911
926
|
async setPassword(proto, user, password, options) {
|
|
912
927
|
if (!user.id)
|
|
913
928
|
throw Error('Invalid user object');
|
|
914
929
|
if (_.isEmpty(password))
|
|
915
930
|
throw Error('Invalid password');
|
|
931
|
+
const { maxPasswordHistory, validatorCallback, } = this.options.passwordPolicy || {};
|
|
932
|
+
if (validatorCallback) {
|
|
933
|
+
const isValid = await validatorCallback(password, user);
|
|
934
|
+
if (!isValid)
|
|
935
|
+
throw Error('Password does not meet the policy requirements');
|
|
936
|
+
}
|
|
916
937
|
const { alg, ...opts } = this.options.passwordHashOptions;
|
|
917
938
|
const hashed = await passwordHash(alg, password, opts);
|
|
939
|
+
let history = [];
|
|
940
|
+
if (maxPasswordHistory && maxPasswordHistory > 0) {
|
|
941
|
+
const _user = await proto.InsecureQuery('User')
|
|
942
|
+
.equalTo('_id', user.id)
|
|
943
|
+
.includes('_id', 'password_history')
|
|
944
|
+
.first(options);
|
|
945
|
+
history = _.slice(_user?.get('password_history') ?? [], 0, maxPasswordHistory);
|
|
946
|
+
for (const entry of history) {
|
|
947
|
+
const { alg, ...opts } = entry;
|
|
948
|
+
if (await verifyPassword(alg, password, opts)) {
|
|
949
|
+
throw Error('Cannot reuse previous passwords');
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
918
953
|
await proto.InsecureQuery('User')
|
|
919
954
|
.equalTo('_id', user.id)
|
|
920
955
|
.includes('_id')
|
|
921
956
|
.updateOne({
|
|
922
957
|
password: { $set: hashed },
|
|
958
|
+
password_history: {
|
|
959
|
+
$set: maxPasswordHistory && maxPasswordHistory > 0
|
|
960
|
+
? _.slice([{ ...hashed, password }, ...history], 0, maxPasswordHistory)
|
|
961
|
+
: [],
|
|
962
|
+
},
|
|
963
|
+
password_changed_at: { $set: new Date() },
|
|
923
964
|
}, options);
|
|
924
965
|
}
|
|
925
966
|
async unsetPassword(proto, user, options) {
|
|
@@ -930,6 +971,7 @@ class ProtoInternal {
|
|
|
930
971
|
.includes('_id')
|
|
931
972
|
.updateOne({
|
|
932
973
|
password: { $set: {} },
|
|
974
|
+
password_changed_at: { $set: new Date() },
|
|
933
975
|
}, options);
|
|
934
976
|
}
|
|
935
977
|
async updateFile(proto, object, options) {
|
|
@@ -947,8 +989,8 @@ class ProtoInternal {
|
|
|
947
989
|
}
|
|
948
990
|
return object;
|
|
949
991
|
}
|
|
950
|
-
|
|
951
|
-
const { nonce, attributes, maxUploadSize, } = (_.isString(token) ? this.
|
|
992
|
+
verifyUploadToken(proto, token, isMaster) {
|
|
993
|
+
const { nonce, attributes, maxUploadSize, } = (_.isString(token) ? this.jwtVerify(token, 'upload') ?? {} : {});
|
|
952
994
|
if (!isMaster && !nonce)
|
|
953
995
|
throw Error('Upload token is required');
|
|
954
996
|
return {
|
|
@@ -961,7 +1003,7 @@ class ProtoInternal {
|
|
|
961
1003
|
const data = object[PVK].extra.data;
|
|
962
1004
|
if (_.isNil(data))
|
|
963
1005
|
throw Error('Invalid file object');
|
|
964
|
-
const { nonce, attributes = {}, maxUploadSize = this.options.maxUploadSize, } = options?.uploadToken ? this.
|
|
1006
|
+
const { nonce, attributes = {}, maxUploadSize = this.options.maxUploadSize, } = options?.uploadToken ? this.verifyUploadToken(proto, options.uploadToken, options.master) : {};
|
|
965
1007
|
if (nonce) {
|
|
966
1008
|
const found = await proto.Query('File').equalTo('nonce', nonce).first({ master: true });
|
|
967
1009
|
if (found)
|
|
@@ -1068,7 +1110,7 @@ class ProtoInternal {
|
|
|
1068
1110
|
})();
|
|
1069
1111
|
return jwt.sign(payload, this.options.jwtToken, opts);
|
|
1070
1112
|
}
|
|
1071
|
-
|
|
1113
|
+
jwtVerify(token, options = {}) {
|
|
1072
1114
|
try {
|
|
1073
1115
|
const opts = (() => {
|
|
1074
1116
|
switch (options) {
|
|
@@ -1204,7 +1246,7 @@ class ProtoInternal {
|
|
|
1204
1246
|
obj.set('data', params);
|
|
1205
1247
|
obj.set('user', user);
|
|
1206
1248
|
await obj.save({ master: true });
|
|
1207
|
-
this.jobRunner.
|
|
1249
|
+
this.jobRunner.executeJob(proto);
|
|
1208
1250
|
return obj;
|
|
1209
1251
|
}
|
|
1210
1252
|
}
|
|
@@ -1266,7 +1308,7 @@ class JobRunner {
|
|
|
1266
1308
|
job.set('completedAt', new Date());
|
|
1267
1309
|
await job.save({ master: true });
|
|
1268
1310
|
}
|
|
1269
|
-
async
|
|
1311
|
+
async executeJob(proto) {
|
|
1270
1312
|
if (this._running || this._stopped)
|
|
1271
1313
|
return;
|
|
1272
1314
|
this._running = true;
|
|
@@ -1296,7 +1338,7 @@ class JobRunner {
|
|
|
1296
1338
|
finally {
|
|
1297
1339
|
clearInterval(timer);
|
|
1298
1340
|
}
|
|
1299
|
-
this.
|
|
1341
|
+
this.executeJob(proto);
|
|
1300
1342
|
})();
|
|
1301
1343
|
}
|
|
1302
1344
|
this._running = false;
|
|
@@ -1330,7 +1372,7 @@ class JobRunner {
|
|
|
1330
1372
|
const _sessionWithToken = async (proto, token) => {
|
|
1331
1373
|
if (_.isEmpty(token))
|
|
1332
1374
|
return;
|
|
1333
|
-
const payload = proto[PVK].
|
|
1375
|
+
const payload = proto[PVK].jwtVerify(token, 'login') ?? {};
|
|
1334
1376
|
if (!_.isString(payload.sessionId) || _.isEmpty(payload.sessionId))
|
|
1335
1377
|
return;
|
|
1336
1378
|
const session = await proto.Query('_Session')
|
|
@@ -1489,8 +1531,8 @@ const scheduleOp = {
|
|
|
1489
1531
|
expireDocument: async (proto) => {
|
|
1490
1532
|
await proto.gc();
|
|
1491
1533
|
},
|
|
1492
|
-
|
|
1493
|
-
proto[PVK].jobRunner.
|
|
1534
|
+
executeJob: async (proto) => {
|
|
1535
|
+
proto[PVK].jobRunner.executeJob(proto);
|
|
1494
1536
|
},
|
|
1495
1537
|
};
|
|
1496
1538
|
const schedule = (proto) => {
|
|
@@ -1699,8 +1741,8 @@ class ProtoService extends ProtoType {
|
|
|
1699
1741
|
if (req.res)
|
|
1700
1742
|
await signUser(this, req.res, undefined, options);
|
|
1701
1743
|
}
|
|
1702
|
-
|
|
1703
|
-
return this[PVK].
|
|
1744
|
+
verifyPassword(user, password, options) {
|
|
1745
|
+
return this[PVK].verifyPassword(this, user, password, options);
|
|
1704
1746
|
}
|
|
1705
1747
|
setPassword(user, password, options) {
|
|
1706
1748
|
return this[PVK].setPassword(this, user, password, options);
|
|
@@ -1787,8 +1829,8 @@ class ProtoService extends ProtoType {
|
|
|
1787
1829
|
jwtSign(payload, options) {
|
|
1788
1830
|
return this[PVK].jwtSign(payload, options);
|
|
1789
1831
|
}
|
|
1790
|
-
|
|
1791
|
-
return this[PVK].
|
|
1832
|
+
jwtVerify(token, options = {}) {
|
|
1833
|
+
return this[PVK].jwtVerify(token, options);
|
|
1792
1834
|
}
|
|
1793
1835
|
notify(data) {
|
|
1794
1836
|
return this[PVK].notify(this, data);
|
|
@@ -1974,7 +2016,7 @@ const verifyRelatedBy = (relatedBy) => {
|
|
|
1974
2016
|
var classesRoute = (router, proto) => {
|
|
1975
2017
|
const defaultHandler = async (req) => {
|
|
1976
2018
|
const { name } = req.params;
|
|
1977
|
-
const { operation, random, attributes, update, setOnInsert, relatedBy, silent, ...options } = deserialize(req.body);
|
|
2019
|
+
const { operation, accumulators, random, attributes, update, setOnInsert, relatedBy, silent, ...options } = deserialize(req.body);
|
|
1978
2020
|
verifyRelatedBy(relatedBy);
|
|
1979
2021
|
const payload = proto.connect(req);
|
|
1980
2022
|
const query = relatedBy ? payload.Relation(payload.Object(relatedBy.className, relatedBy.id), relatedBy.key) : payload.Query(name);
|
|
@@ -1995,15 +2037,16 @@ var classesRoute = (router, proto) => {
|
|
|
1995
2037
|
query[PVK].options.limit = query[PVK].options.limit ?? maxFetchLimit;
|
|
1996
2038
|
if (query[PVK].options.limit > maxFetchLimit)
|
|
1997
2039
|
throw Error('Query over limit');
|
|
1998
|
-
return
|
|
2040
|
+
return query.find(opts);
|
|
1999
2041
|
}
|
|
2042
|
+
case 'groupFind': return query.groupFind(accumulators, opts);
|
|
2000
2043
|
case 'random':
|
|
2001
2044
|
{
|
|
2002
2045
|
const maxFetchLimit = payload[PVK].options.maxFetchLimit;
|
|
2003
2046
|
query[PVK].options.limit = query[PVK].options.limit ?? maxFetchLimit;
|
|
2004
2047
|
if (query[PVK].options.limit > maxFetchLimit)
|
|
2005
2048
|
throw Error('Query over limit');
|
|
2006
|
-
return
|
|
2049
|
+
return query.random(random, opts);
|
|
2007
2050
|
}
|
|
2008
2051
|
case 'nonrefs':
|
|
2009
2052
|
{
|
|
@@ -2011,7 +2054,7 @@ var classesRoute = (router, proto) => {
|
|
|
2011
2054
|
query[PVK].options.limit = query[PVK].options.limit ?? maxFetchLimit;
|
|
2012
2055
|
if (query[PVK].options.limit > maxFetchLimit)
|
|
2013
2056
|
throw Error('Query over limit');
|
|
2014
|
-
return
|
|
2057
|
+
return query.nonrefs(opts);
|
|
2015
2058
|
}
|
|
2016
2059
|
case 'insert': return query.insert(attributes, opts);
|
|
2017
2060
|
case 'insertMany': return query.insertMany(attributes, opts);
|
|
@@ -2281,7 +2324,7 @@ var filesRoute = (router, proto) => {
|
|
|
2281
2324
|
await response(res, async () => {
|
|
2282
2325
|
const payload = proto.connect(req);
|
|
2283
2326
|
const uploadToken = req.header(UPLOAD_TOKEN_HEADER_NAME);
|
|
2284
|
-
const { maxUploadSize } = payload[PVK].
|
|
2327
|
+
const { maxUploadSize } = payload[PVK].verifyUploadToken(payload, uploadToken, payload.isMaster);
|
|
2285
2328
|
const { attributes, file } = await decodeFormStream(req, (file, info) => proto.fileStorage.create(proto, file, info, maxUploadSize));
|
|
2286
2329
|
try {
|
|
2287
2330
|
const obj = payload.Object('File');
|