@aeriajs/builtins 0.0.276 → 0.0.278
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/dist/authentication.js +6 -11
- package/dist/collections/file/description.js +10 -14
- package/dist/collections/file/download.js +21 -25
- package/dist/collections/file/index.js +15 -18
- package/dist/collections/file/insert.js +8 -12
- package/dist/collections/file/remove.js +7 -11
- package/dist/collections/file/removeAll.js +5 -9
- package/dist/collections/index.js +4 -20
- package/dist/collections/log/index.js +5 -8
- package/dist/collections/resourceUsage/index.js +2 -5
- package/dist/collections/user/activate.js +31 -35
- package/dist/collections/user/authenticate.js +29 -33
- package/dist/collections/user/createAccount.js +25 -29
- package/dist/collections/user/description.js +5 -8
- package/dist/collections/user/editProfile.js +12 -16
- package/dist/collections/user/getActivationLink.js +21 -26
- package/dist/collections/user/getCurrentUser.js +12 -16
- package/dist/collections/user/getInfo.js +22 -26
- package/dist/collections/user/getRedefinePasswordLink.js +15 -19
- package/dist/collections/user/index.d.ts +10 -10
- package/dist/collections/user/index.js +39 -42
- package/dist/collections/user/insert.js +16 -20
- package/dist/collections/user/redefinePassword.js +31 -35
- package/dist/functions/describe.js +21 -25
- package/dist/functions/index.js +1 -17
- package/dist/index.d.ts +15 -15
- package/dist/index.js +11 -29
- package/package.json +11 -17
- package/dist/authentication.mjs +0 -58
- package/dist/collections/file/description.mjs +0 -75
- package/dist/collections/file/download.mjs +0 -115
- package/dist/collections/file/index.mjs +0 -59
- package/dist/collections/file/insert.mjs +0 -44
- package/dist/collections/file/remove.mjs +0 -21
- package/dist/collections/file/removeAll.mjs +0 -22
- package/dist/collections/index.mjs +0 -5
- package/dist/collections/log/index.mjs +0 -55
- package/dist/collections/resourceUsage/index.mjs +0 -39
- package/dist/collections/user/activate.mjs +0 -119
- package/dist/collections/user/authenticate.mjs +0 -165
- package/dist/collections/user/createAccount.mjs +0 -93
- package/dist/collections/user/description.mjs +0 -149
- package/dist/collections/user/editProfile.mjs +0 -52
- package/dist/collections/user/getActivationLink.mjs +0 -88
- package/dist/collections/user/getCurrentUser.mjs +0 -57
- package/dist/collections/user/getInfo.mjs +0 -85
- package/dist/collections/user/getRedefinePasswordLink.mjs +0 -63
- package/dist/collections/user/index.mjs +0 -71
- package/dist/collections/user/insert.mjs +0 -70
- package/dist/collections/user/redefinePassword.mjs +0 -110
- package/dist/functions/describe.mjs +0 -95
- package/dist/functions/index.mjs +0 -2
- package/dist/index.mjs +0 -21
package/dist/authentication.mjs
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { signToken } from "@aeriajs/core";
|
|
3
|
-
export const AuthenticationError = {
|
|
4
|
-
InvalidCredentials: "INVALID_CREDENTIALS",
|
|
5
|
-
InactiveUser: "INACTIVE_USER"
|
|
6
|
-
};
|
|
7
|
-
export const successfulAuthentication = async (user, context) => {
|
|
8
|
-
const tokenContent = {
|
|
9
|
-
sub: user._id,
|
|
10
|
-
roles: user.roles,
|
|
11
|
-
picture: user.picture,
|
|
12
|
-
userinfo: {}
|
|
13
|
-
};
|
|
14
|
-
if (context.config.security.authenticationRateLimiting) {
|
|
15
|
-
}
|
|
16
|
-
if (context.config.security.logSuccessfulAuthentications) {
|
|
17
|
-
await context.log("successful authentication", {
|
|
18
|
-
email: user.email,
|
|
19
|
-
roles: user.roles,
|
|
20
|
-
_id: user._id
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
if (context.config.tokenUserProperties) {
|
|
24
|
-
const userinfo = {};
|
|
25
|
-
for (const prop of context.config.tokenUserProperties) {
|
|
26
|
-
userinfo[prop] = user[prop];
|
|
27
|
-
}
|
|
28
|
-
tokenContent.userinfo = userinfo;
|
|
29
|
-
}
|
|
30
|
-
const token = await signToken(tokenContent);
|
|
31
|
-
return {
|
|
32
|
-
user,
|
|
33
|
-
token: {
|
|
34
|
-
type: "bearer",
|
|
35
|
-
content: token
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
export const defaultSuccessfulAuthentication = async () => {
|
|
40
|
-
const token = await signToken({
|
|
41
|
-
sub: null,
|
|
42
|
-
roles: ["root"],
|
|
43
|
-
userinfo: {}
|
|
44
|
-
});
|
|
45
|
-
return {
|
|
46
|
-
user: {
|
|
47
|
-
_id: null,
|
|
48
|
-
name: "God Mode",
|
|
49
|
-
email: "",
|
|
50
|
-
roles: ["root"],
|
|
51
|
-
active: true
|
|
52
|
-
},
|
|
53
|
-
token: {
|
|
54
|
-
type: "bearer",
|
|
55
|
-
content: token
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { ObjectId } from "mongodb";
|
|
3
|
-
import { defineDescription } from "@aeriajs/core";
|
|
4
|
-
import { getConfig } from "@aeriajs/entrypoint";
|
|
5
|
-
const timestamp = (lastModified) => lastModified instanceof Date ? new Date(lastModified).getTime() : "fresh";
|
|
6
|
-
export const getFileLink = async (fileId) => {
|
|
7
|
-
const config = await getConfig();
|
|
8
|
-
return `${config.publicUrl || ""}/file/${fileId}`;
|
|
9
|
-
};
|
|
10
|
-
export const description = defineDescription({
|
|
11
|
-
$id: "file",
|
|
12
|
-
icon: "paperclip",
|
|
13
|
-
owned: "always",
|
|
14
|
-
presets: ["owned"],
|
|
15
|
-
indexes: [
|
|
16
|
-
"name",
|
|
17
|
-
"link",
|
|
18
|
-
"type",
|
|
19
|
-
"size"
|
|
20
|
-
],
|
|
21
|
-
properties: {
|
|
22
|
-
type: {
|
|
23
|
-
type: "string"
|
|
24
|
-
},
|
|
25
|
-
size: {
|
|
26
|
-
type: "number"
|
|
27
|
-
},
|
|
28
|
-
last_modified: {
|
|
29
|
-
type: "string",
|
|
30
|
-
format: "date-time"
|
|
31
|
-
},
|
|
32
|
-
name: {
|
|
33
|
-
type: "string"
|
|
34
|
-
},
|
|
35
|
-
absolute_path: {
|
|
36
|
-
type: "string"
|
|
37
|
-
},
|
|
38
|
-
relative_path: {
|
|
39
|
-
type: "string"
|
|
40
|
-
},
|
|
41
|
-
immutable: {
|
|
42
|
-
type: "boolean"
|
|
43
|
-
},
|
|
44
|
-
link: {
|
|
45
|
-
type: "getter",
|
|
46
|
-
getter: async (doc) => {
|
|
47
|
-
if ("_id" in doc && "last_modified" in doc && doc._id instanceof ObjectId) {
|
|
48
|
-
return `${await getFileLink(doc._id)}/${timestamp(doc.last_modified)}`;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
download_link: {
|
|
53
|
-
type: "getter",
|
|
54
|
-
getter: async (doc) => {
|
|
55
|
-
if ("_id" in doc && "last_modified" in doc && doc._id instanceof ObjectId) {
|
|
56
|
-
return `${await getFileLink(doc._id)}/download/${timestamp(doc.last_modified)}`;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
actions: {
|
|
62
|
-
deleteAll: {
|
|
63
|
-
label: "Remover",
|
|
64
|
-
ask: true,
|
|
65
|
-
selection: true
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
individualActions: {
|
|
69
|
-
remove: {
|
|
70
|
-
label: "Remover",
|
|
71
|
-
icon: "trash",
|
|
72
|
-
ask: true
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { HTTPStatus, ACError, defineContract, endpointErrorSchema } from "@aeriajs/types";
|
|
3
|
-
import { ObjectId } from "@aeriajs/core";
|
|
4
|
-
import * as fs from "node:fs";
|
|
5
|
-
export const DownloadError = {
|
|
6
|
-
RangeNotSatisfiable: "RANGE_NOT_SATISFIABLE"
|
|
7
|
-
};
|
|
8
|
-
export const downloadContract = defineContract({
|
|
9
|
-
payload: {
|
|
10
|
-
type: "object",
|
|
11
|
-
required: ["fileId"],
|
|
12
|
-
properties: {
|
|
13
|
-
fileId: {
|
|
14
|
-
type: "string"
|
|
15
|
-
},
|
|
16
|
-
options: {
|
|
17
|
-
type: "array",
|
|
18
|
-
items: {
|
|
19
|
-
enum: [
|
|
20
|
-
"picture",
|
|
21
|
-
"download"
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
noHeaders: {
|
|
26
|
-
type: "boolean"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
response: [
|
|
31
|
-
endpointErrorSchema({
|
|
32
|
-
httpStatus: [
|
|
33
|
-
HTTPStatus.NotFound,
|
|
34
|
-
HTTPStatus.RangeNotSatisfiable
|
|
35
|
-
],
|
|
36
|
-
code: [
|
|
37
|
-
ACError.ResourceNotFound,
|
|
38
|
-
DownloadError.RangeNotSatisfiable
|
|
39
|
-
]
|
|
40
|
-
}),
|
|
41
|
-
{
|
|
42
|
-
type: "object",
|
|
43
|
-
additionalProperties: true
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
});
|
|
47
|
-
export const download = async (payload, context) => {
|
|
48
|
-
const { fileId, options = [] } = payload;
|
|
49
|
-
const file = await context.collection.model.findOne({
|
|
50
|
-
_id: new ObjectId(fileId)
|
|
51
|
-
}, {
|
|
52
|
-
projection: {
|
|
53
|
-
absolute_path: 1,
|
|
54
|
-
name: 1,
|
|
55
|
-
type: 1
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
if (!file) {
|
|
59
|
-
if (!payload.noHeaders) {
|
|
60
|
-
context.response.writeHead(HTTPStatus.NotFound, {
|
|
61
|
-
"content-type": "application/json"
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
return context.error(HTTPStatus.NotFound, {
|
|
65
|
-
code: ACError.ResourceNotFound
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
let stat;
|
|
69
|
-
try {
|
|
70
|
-
stat = await fs.promises.stat(file.absolute_path);
|
|
71
|
-
} catch (e) {
|
|
72
|
-
context.response.writeHead(404, {
|
|
73
|
-
"content-type": "application/json"
|
|
74
|
-
});
|
|
75
|
-
return context.error(HTTPStatus.NotFound, {
|
|
76
|
-
code: ACError.ResourceNotFound
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
const range = context.request.headers.range;
|
|
80
|
-
if (typeof range === "string") {
|
|
81
|
-
const parts = range.replace(/bytes=/, "").split("-");
|
|
82
|
-
const start = parseInt(parts[0]);
|
|
83
|
-
const end = parts[1] ? parseInt(parts[1]) : stat.size - 1;
|
|
84
|
-
if (start >= stat.size || end >= stat.size) {
|
|
85
|
-
context.response.writeHead(HTTPStatus.RangeNotSatisfiable, {
|
|
86
|
-
"content-type": "application/json",
|
|
87
|
-
"content-range": `bytes */${stat.size}`
|
|
88
|
-
});
|
|
89
|
-
return context.error(HTTPStatus.RangeNotSatisfiable, {
|
|
90
|
-
code: DownloadError.RangeNotSatisfiable
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
const chunkSize = end - start + 1;
|
|
94
|
-
if (!payload.noHeaders) {
|
|
95
|
-
context.response.writeHead(206, {
|
|
96
|
-
"accept-ranges": "bytes",
|
|
97
|
-
"content-range": `bytes ${start}-${end}/${stat.size}`,
|
|
98
|
-
"content-length": chunkSize,
|
|
99
|
-
"content-type": file.type,
|
|
100
|
-
"content-disposition": `${options.includes("download") ? "attachment; " : ""}name=${encodeURI(file.name)}`
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return fs.createReadStream(file.absolute_path, {
|
|
104
|
-
start,
|
|
105
|
-
end
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
if (!payload.noHeaders) {
|
|
109
|
-
context.response.writeHead(HTTPStatus.Ok, {
|
|
110
|
-
"content-type": file.type,
|
|
111
|
-
"content-disposition": `${options.includes("download") ? "attachment; " : ""}name=${encodeURI(file.name)}`
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
return fs.createReadStream(file.absolute_path);
|
|
115
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { defineCollection, get } from "@aeriajs/core";
|
|
3
|
-
import { description } from "./description.mjs";
|
|
4
|
-
import { insert } from "./insert.mjs";
|
|
5
|
-
import { download, downloadContract } from "./download.mjs";
|
|
6
|
-
import { remove } from "./remove.mjs";
|
|
7
|
-
import { removeAll } from "./removeAll.mjs";
|
|
8
|
-
export const tempFile = defineCollection({
|
|
9
|
-
description: {
|
|
10
|
-
$id: "tempFile",
|
|
11
|
-
icon: "file",
|
|
12
|
-
hidden: true,
|
|
13
|
-
temporary: {
|
|
14
|
-
index: "created_at",
|
|
15
|
-
expireAfterSeconds: 3600
|
|
16
|
-
},
|
|
17
|
-
properties: {
|
|
18
|
-
created_at: {
|
|
19
|
-
type: "string",
|
|
20
|
-
format: "date-time"
|
|
21
|
-
},
|
|
22
|
-
absolute_path: {
|
|
23
|
-
type: "string"
|
|
24
|
-
},
|
|
25
|
-
size: {
|
|
26
|
-
type: "number"
|
|
27
|
-
},
|
|
28
|
-
mime: {
|
|
29
|
-
type: "number"
|
|
30
|
-
},
|
|
31
|
-
collection: {
|
|
32
|
-
type: "string"
|
|
33
|
-
},
|
|
34
|
-
filename: {
|
|
35
|
-
type: "string"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
export const file = defineCollection({
|
|
41
|
-
description,
|
|
42
|
-
functions: {
|
|
43
|
-
get,
|
|
44
|
-
insert,
|
|
45
|
-
download,
|
|
46
|
-
remove,
|
|
47
|
-
removeAll
|
|
48
|
-
},
|
|
49
|
-
exposedFunctions: {
|
|
50
|
-
get: "unauthenticated",
|
|
51
|
-
insert: true,
|
|
52
|
-
download: "unauthenticated",
|
|
53
|
-
remove: true,
|
|
54
|
-
removeAll: true
|
|
55
|
-
},
|
|
56
|
-
contracts: {
|
|
57
|
-
download: downloadContract
|
|
58
|
-
}
|
|
59
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { writeFile, unlink } from "node:fs/promises";
|
|
4
|
-
import { insert as originalInsert } from "@aeriajs/core";
|
|
5
|
-
export const insert = async (payload, context) => {
|
|
6
|
-
if (!context.token.authenticated) {
|
|
7
|
-
throw new Error("");
|
|
8
|
-
}
|
|
9
|
-
const { content, ...what } = Object.assign({}, payload.what);
|
|
10
|
-
if (context.token.sub) {
|
|
11
|
-
what.owner = context.token.sub;
|
|
12
|
-
}
|
|
13
|
-
const extension = what.name.split(".").at(-1);
|
|
14
|
-
if (!context.config.storage) {
|
|
15
|
-
throw new Error("config.storage is not set");
|
|
16
|
-
}
|
|
17
|
-
const tempPath = context.config.storage.tempFs || context.config.storage.fs;
|
|
18
|
-
if (!tempPath) {
|
|
19
|
-
throw new Error("config.storage.fs and config.storage.tempFs are not set");
|
|
20
|
-
}
|
|
21
|
-
if (!extension) {
|
|
22
|
-
throw new Error("filename lacks extension");
|
|
23
|
-
}
|
|
24
|
-
const oldFile = await context.collection.model.findOne(
|
|
25
|
-
{
|
|
26
|
-
_id: payload.what._id
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
projection: {
|
|
30
|
-
absolute_path: 1
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
if (oldFile && oldFile.absolute_path) {
|
|
35
|
-
await unlink(oldFile.absolute_path).catch(console.trace);
|
|
36
|
-
}
|
|
37
|
-
const filenameHash = createHash("sha1").update(what.name + Date.now()).digest("hex");
|
|
38
|
-
what.absolute_path = `${tempPath}/${filenameHash}.${extension}`;
|
|
39
|
-
await writeFile(what.absolute_path, Buffer.from(content.split(",").at(-1), "base64"));
|
|
40
|
-
return originalInsert({
|
|
41
|
-
...payload,
|
|
42
|
-
what
|
|
43
|
-
}, context);
|
|
44
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { Result } from "@aeriajs/types";
|
|
3
|
-
import { remove as originalRemove, get } from "@aeriajs/core";
|
|
4
|
-
import * as fs from "node:fs/promises";
|
|
5
|
-
export const remove = async (payload, context) => {
|
|
6
|
-
const { error, result: file } = await get({
|
|
7
|
-
filters: {
|
|
8
|
-
_id: payload.filters._id
|
|
9
|
-
},
|
|
10
|
-
project: ["absolute_path"]
|
|
11
|
-
}, context);
|
|
12
|
-
if (error) {
|
|
13
|
-
return Result.error(error);
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
await fs.unlink(file.absolute_path);
|
|
17
|
-
} catch (err) {
|
|
18
|
-
console.trace(err);
|
|
19
|
-
}
|
|
20
|
-
return originalRemove(payload, context);
|
|
21
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { ObjectId, removeAll as originalRemoveAll } from "@aeriajs/core";
|
|
3
|
-
import * as fs from "node:fs/promises";
|
|
4
|
-
export const removeAll = async (payload, context) => {
|
|
5
|
-
const files = context.collection.model.find({
|
|
6
|
-
_id: {
|
|
7
|
-
$in: payload.filters.map((oid) => new ObjectId(oid))
|
|
8
|
-
}
|
|
9
|
-
}, {
|
|
10
|
-
projection: {
|
|
11
|
-
absolute_path: 1
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
for await (const file of files) {
|
|
15
|
-
try {
|
|
16
|
-
await fs.unlink(file.absolute_path);
|
|
17
|
-
} catch (err) {
|
|
18
|
-
console.trace(err);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return originalRemoveAll(payload, context);
|
|
22
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { defineCollection, get, getAll, insert } from "@aeriajs/core";
|
|
3
|
-
export const log = defineCollection({
|
|
4
|
-
description: {
|
|
5
|
-
$id: "log",
|
|
6
|
-
icon: "magnifying-glass",
|
|
7
|
-
required: [
|
|
8
|
-
"context",
|
|
9
|
-
"message"
|
|
10
|
-
],
|
|
11
|
-
table: [
|
|
12
|
-
"owner",
|
|
13
|
-
"context",
|
|
14
|
-
"message",
|
|
15
|
-
"created_at"
|
|
16
|
-
],
|
|
17
|
-
properties: {
|
|
18
|
-
owner: {
|
|
19
|
-
// don't use "owned: true", we want it this way
|
|
20
|
-
$ref: "user",
|
|
21
|
-
noForm: true
|
|
22
|
-
},
|
|
23
|
-
context: {
|
|
24
|
-
type: "string"
|
|
25
|
-
},
|
|
26
|
-
message: {
|
|
27
|
-
type: "string"
|
|
28
|
-
},
|
|
29
|
-
details: {
|
|
30
|
-
type: "object",
|
|
31
|
-
additionalProperties: true
|
|
32
|
-
},
|
|
33
|
-
created_at: {
|
|
34
|
-
type: "string",
|
|
35
|
-
format: "date-time"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
presets: ["view"],
|
|
39
|
-
filters: [
|
|
40
|
-
"context",
|
|
41
|
-
"message",
|
|
42
|
-
"owner"
|
|
43
|
-
]
|
|
44
|
-
},
|
|
45
|
-
functions: {
|
|
46
|
-
get,
|
|
47
|
-
getAll,
|
|
48
|
-
insert
|
|
49
|
-
},
|
|
50
|
-
exposedFunctions: {
|
|
51
|
-
get: true,
|
|
52
|
-
getAll: true,
|
|
53
|
-
insert: true
|
|
54
|
-
}
|
|
55
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { defineCollection } from "@aeriajs/core";
|
|
3
|
-
export const resourceUsage = defineCollection({
|
|
4
|
-
description: {
|
|
5
|
-
$id: "resourceUsage",
|
|
6
|
-
icon: "wrench",
|
|
7
|
-
required: ["usage"],
|
|
8
|
-
properties: {
|
|
9
|
-
user: {
|
|
10
|
-
$ref: "user"
|
|
11
|
-
},
|
|
12
|
-
address: {
|
|
13
|
-
type: "string"
|
|
14
|
-
},
|
|
15
|
-
usage: {
|
|
16
|
-
type: "object",
|
|
17
|
-
additionalProperties: {
|
|
18
|
-
type: "object",
|
|
19
|
-
properties: {
|
|
20
|
-
hits: {
|
|
21
|
-
type: "integer"
|
|
22
|
-
},
|
|
23
|
-
points: {
|
|
24
|
-
type: "integer"
|
|
25
|
-
},
|
|
26
|
-
last_reach: {
|
|
27
|
-
type: "string",
|
|
28
|
-
format: "date-time"
|
|
29
|
-
},
|
|
30
|
-
last_maximum_reach: {
|
|
31
|
-
type: "string",
|
|
32
|
-
format: "date-time"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { decodeToken, ObjectId } from "@aeriajs/core";
|
|
3
|
-
import { Result, ACError, HTTPStatus, defineContract, resultSchema, endpointErrorSchema } from "@aeriajs/types";
|
|
4
|
-
import * as bcrypt from "bcryptjs";
|
|
5
|
-
export const ActivationError = {
|
|
6
|
-
UserNotFound: "USER_NOT_FOUND",
|
|
7
|
-
AlreadyActiveUser: "ALREADY_ACTIVE_USER",
|
|
8
|
-
InvalidLink: "INVALID_LINK",
|
|
9
|
-
InvalidToken: "INVALID_TOKEN"
|
|
10
|
-
};
|
|
11
|
-
export const activateContract = defineContract({
|
|
12
|
-
payload: {
|
|
13
|
-
type: "object",
|
|
14
|
-
required: [],
|
|
15
|
-
properties: {
|
|
16
|
-
password: {
|
|
17
|
-
type: "string"
|
|
18
|
-
},
|
|
19
|
-
userId: {
|
|
20
|
-
type: "string"
|
|
21
|
-
},
|
|
22
|
-
token: {
|
|
23
|
-
type: "string"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
response: [
|
|
28
|
-
endpointErrorSchema({
|
|
29
|
-
httpStatus: [
|
|
30
|
-
HTTPStatus.NotFound,
|
|
31
|
-
HTTPStatus.Forbidden,
|
|
32
|
-
HTTPStatus.Unauthorized,
|
|
33
|
-
HTTPStatus.UnprocessableContent
|
|
34
|
-
],
|
|
35
|
-
code: [
|
|
36
|
-
ACError.ResourceNotFound,
|
|
37
|
-
ACError.MalformedInput,
|
|
38
|
-
ActivationError.AlreadyActiveUser,
|
|
39
|
-
ActivationError.InvalidLink,
|
|
40
|
-
ActivationError.InvalidToken,
|
|
41
|
-
ActivationError.UserNotFound
|
|
42
|
-
]
|
|
43
|
-
}),
|
|
44
|
-
resultSchema({
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
userId: {
|
|
48
|
-
type: "string",
|
|
49
|
-
format: "objectid"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
]
|
|
54
|
-
});
|
|
55
|
-
export const activate = async (payload, context) => {
|
|
56
|
-
const {
|
|
57
|
-
userId,
|
|
58
|
-
token,
|
|
59
|
-
password
|
|
60
|
-
} = payload;
|
|
61
|
-
if (!context.config.secret) {
|
|
62
|
-
throw new Error("config.secret is not set");
|
|
63
|
-
}
|
|
64
|
-
if (!userId || !token) {
|
|
65
|
-
return context.error(HTTPStatus.NotFound, {
|
|
66
|
-
code: ActivationError.InvalidLink
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
const user = await context.collection.model.findOne({
|
|
70
|
-
_id: new ObjectId(userId)
|
|
71
|
-
}, {
|
|
72
|
-
projection: {
|
|
73
|
-
password: 1,
|
|
74
|
-
active: 1
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
if (!user) {
|
|
78
|
-
return context.error(HTTPStatus.NotFound, {
|
|
79
|
-
code: ActivationError.UserNotFound
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
if (user.active) {
|
|
83
|
-
return context.error(HTTPStatus.Forbidden, {
|
|
84
|
-
code: ActivationError.AlreadyActiveUser
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
const decoded = await decodeToken(token, context.config.secret);
|
|
88
|
-
if (!decoded) {
|
|
89
|
-
return context.error(HTTPStatus.Unauthorized, {
|
|
90
|
-
code: ActivationError.InvalidToken
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
if (!user.password) {
|
|
94
|
-
if (!password) {
|
|
95
|
-
return context.error(HTTPStatus.UnprocessableContent, {
|
|
96
|
-
code: ACError.MalformedInput
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
await context.collection.model.updateOne({
|
|
100
|
-
_id: user._id
|
|
101
|
-
}, {
|
|
102
|
-
$set: {
|
|
103
|
-
active: true,
|
|
104
|
-
password: await bcrypt.hash(password, 10)
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
} else {
|
|
108
|
-
await context.collection.model.updateOne({
|
|
109
|
-
_id: user._id
|
|
110
|
-
}, {
|
|
111
|
-
$set: {
|
|
112
|
-
active: true
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
return Result.result({
|
|
117
|
-
userId: user._id
|
|
118
|
-
});
|
|
119
|
-
};
|