@aeriajs/builtins 0.0.296 → 0.0.297

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.
@@ -37,7 +37,7 @@ export declare const activateContract: {
37
37
  readonly required: readonly ["httpStatus", "code"];
38
38
  readonly properties: {
39
39
  readonly httpStatus: {
40
- readonly enum: [404, 403, 401, 422];
40
+ readonly enum: [400, 404, 403, 401, 422];
41
41
  };
42
42
  readonly code: {
43
43
  readonly enum: ["RESOURCE_NOT_FOUND", "MALFORMED_INPUT", "ALREADY_ACTIVE_USER", "INVALID_LINK", "INVALID_TOKEN", "USER_NOT_FOUND"];
@@ -26,6 +26,7 @@ export const activateContract = defineContract({
26
26
  response: [
27
27
  endpointErrorSchema({
28
28
  httpStatus: [
29
+ HTTPStatus.BadRequest,
29
30
  HTTPStatus.NotFound,
30
31
  HTTPStatus.Forbidden,
31
32
  HTTPStatus.Unauthorized,
@@ -57,7 +58,7 @@ export const activate = async (payload, context) => {
57
58
  throw new Error('config.secret is not set');
58
59
  }
59
60
  if (!userId || !token) {
60
- return context.error(HTTPStatus.NotFound, {
61
+ return context.error(HTTPStatus.BadRequest, {
61
62
  code: ActivationError.InvalidLink,
62
63
  });
63
64
  }
@@ -67,6 +68,7 @@ export const activate = async (payload, context) => {
67
68
  projection: {
68
69
  password: 1,
69
70
  active: 1,
71
+ activation_timestamp: 1,
70
72
  },
71
73
  });
72
74
  if (!user) {
@@ -79,7 +81,7 @@ export const activate = async (payload, context) => {
79
81
  code: ActivationError.AlreadyActiveUser,
80
82
  });
81
83
  }
82
- const { error } = await decodeToken(token, context.config.secret);
84
+ const { error } = await decodeToken(token, `${context.config.secret}:${user.activation_timestamp?.getTime() || ''}`);
83
85
  if (error) {
84
86
  return context.error(HTTPStatus.Unauthorized, {
85
87
  code: ActivationError.InvalidToken,
@@ -97,6 +99,7 @@ export const activate = async (payload, context) => {
97
99
  $set: {
98
100
  active: true,
99
101
  password: await bcrypt.hash(password, 10),
102
+ activation_timestamp: undefined,
100
103
  },
101
104
  });
102
105
  }
@@ -106,6 +109,7 @@ export const activate = async (payload, context) => {
106
109
  }, {
107
110
  $set: {
108
111
  active: true,
112
+ activation_timestamp: undefined,
109
113
  },
110
114
  });
111
115
  }
@@ -62,6 +62,10 @@ export declare const createAccountContract: {
62
62
  readonly type: "string";
63
63
  readonly format: "date-time";
64
64
  };
65
+ readonly activation_timestamp: {
66
+ readonly type: "string";
67
+ readonly format: "date-time";
68
+ };
65
69
  };
66
70
  };
67
71
  readonly response: [{
@@ -63,6 +63,10 @@ export declare const description: {
63
63
  readonly type: "string";
64
64
  readonly format: "date-time";
65
65
  };
66
+ readonly activation_timestamp: {
67
+ readonly type: "string";
68
+ readonly format: "date-time";
69
+ };
66
70
  };
67
71
  readonly presets: readonly ["crud", "duplicate"];
68
72
  readonly layout: {
@@ -88,6 +88,10 @@ export const description = defineDescription({
88
88
  type: 'string',
89
89
  format: 'date-time',
90
90
  },
91
+ activation_timestamp: {
92
+ type: 'string',
93
+ format: 'date-time',
94
+ },
91
95
  },
92
96
  presets: [
93
97
  'crud',
@@ -58,6 +58,10 @@ export declare const editProfileContract: {
58
58
  readonly type: "string";
59
59
  readonly format: "date-time";
60
60
  };
61
+ readonly activation_timestamp: {
62
+ readonly type: "string";
63
+ readonly format: "date-time";
64
+ };
61
65
  };
62
66
  };
63
67
  readonly response: [{
@@ -59,10 +59,10 @@ export declare const getActivationLinkContract: {
59
59
  readonly required: readonly ["httpStatus", "code"];
60
60
  readonly properties: {
61
61
  readonly httpStatus: {
62
- readonly enum: [400, 403];
62
+ readonly enum: [400, 403, 404];
63
63
  };
64
64
  readonly code: {
65
- readonly enum: ["INVALID_LINK", "ALREADY_ACTIVE_USER"];
65
+ readonly enum: ["INVALID_LINK", "USER_NOT_FOUND", "ALREADY_ACTIVE_USER"];
66
66
  };
67
67
  readonly message: {
68
68
  readonly type: "string";
@@ -95,5 +95,4 @@ export declare const getActivationLinkContract: {
95
95
  };
96
96
  }];
97
97
  };
98
- export declare const getActivationToken: (userId: string, context: Context) => Promise<string>;
99
98
  export declare const getActivationLink: ContractToFunction<typeof getActivationLinkContract, Context<typeof description>>;
@@ -1,7 +1,7 @@
1
- import { throwIfError } from '@aeriajs/common';
2
- import { signToken } from '@aeriajs/core';
1
+ import { ObjectId } from 'mongodb';
3
2
  import { Result, HTTPStatus, defineContract, resultSchema, endpointErrorSchema, functionSchemas } from '@aeriajs/types';
4
3
  import { ActivationError } from './activate.js';
4
+ import { getActivationToken } from './getActivationToken.js';
5
5
  export const getActivationLinkContract = defineContract({
6
6
  payload: {
7
7
  type: 'object',
@@ -22,9 +22,11 @@ export const getActivationLinkContract = defineContract({
22
22
  httpStatus: [
23
23
  HTTPStatus.BadRequest,
24
24
  HTTPStatus.Forbidden,
25
+ HTTPStatus.NotFound,
25
26
  ],
26
27
  code: [
27
28
  ActivationError.InvalidLink,
29
+ ActivationError.UserNotFound,
28
30
  ActivationError.AlreadyActiveUser,
29
31
  ],
30
32
  }),
@@ -38,44 +40,44 @@ export const getActivationLinkContract = defineContract({
38
40
  }),
39
41
  ],
40
42
  });
41
- export const getActivationToken = async (userId, context) => {
42
- if (context.calledFunction === 'getActivationToken') {
43
- throw new Error('cannot be called externally');
44
- }
45
- if (!context.config.secret) {
46
- throw new Error('config.secret is not set');
47
- }
48
- const token = throwIfError(await signToken({
49
- data: userId,
50
- }, context.config.secret, {
51
- expiresIn: context.config.security.linkTokenExpiration,
52
- }));
53
- return token;
54
- };
55
43
  export const getActivationLink = async (payload, context) => {
56
44
  if (!context.config.webPublicUrl) {
57
45
  return context.error(HTTPStatus.BadRequest, {
58
46
  code: ActivationError.InvalidLink,
59
47
  });
60
48
  }
61
- const { error, result: user } = await context.collections.user.functions.get({
62
- filters: {
63
- _id: payload.userId,
49
+ const now = new Date();
50
+ const user = await context.collections.user.model.findOneAndUpdate({
51
+ _id: new ObjectId(payload.userId),
52
+ }, {
53
+ $set: {
54
+ activation_link_last_generated_at: now,
55
+ },
56
+ }, {
57
+ returnDocument: 'after',
58
+ projection: {
59
+ active: 1,
60
+ password: 1,
64
61
  },
65
- project: ['active'],
66
62
  });
67
- if (error) {
68
- return Result.error(error);
63
+ if (!user) {
64
+ return Result.error({
65
+ httpStatus: HTTPStatus.NotFound,
66
+ code: ActivationError.UserNotFound,
67
+ });
69
68
  }
70
69
  if (user.active) {
71
70
  return context.error(HTTPStatus.Forbidden, {
72
71
  code: ActivationError.AlreadyActiveUser,
73
72
  });
74
73
  }
75
- const activationToken = await getActivationToken(payload.userId.toString(), context);
74
+ const token = await getActivationToken({
75
+ _id: user._id,
76
+ timestamp: now,
77
+ }, context);
76
78
  const url = new URL(`${context.config.webPublicUrl}/user/activation`);
77
79
  url.searchParams.set('u', payload.userId.toString());
78
- url.searchParams.set('t', activationToken);
80
+ url.searchParams.set('t', token);
79
81
  if (!user.password) {
80
82
  url.searchParams.set('step', 'password');
81
83
  }
@@ -0,0 +1,6 @@
1
+ import type { Context } from '@aeriajs/types';
2
+ import type { ObjectId } from '@aeriajs/core';
3
+ export declare const getActivationToken: (options: {
4
+ _id: ObjectId;
5
+ timestamp: Date;
6
+ }, context: Context) => Promise<string>;
@@ -0,0 +1,13 @@
1
+ import { throwIfError } from '@aeriajs/common';
2
+ import { signToken } from '@aeriajs/core';
3
+ export const getActivationToken = async (options, context) => {
4
+ if (!context.config.secret) {
5
+ throw new Error('config.secret is not set');
6
+ }
7
+ const token = throwIfError(await signToken({
8
+ data: options._id.toString(),
9
+ }, `${context.config.secret}:${options.timestamp}`, {
10
+ expiresIn: context.config.security.linkTokenExpiration,
11
+ }));
12
+ return token;
13
+ };
@@ -59,10 +59,10 @@ export declare const getRedefinePasswordLinkContract: {
59
59
  readonly required: readonly ["httpStatus", "code"];
60
60
  readonly properties: {
61
61
  readonly httpStatus: {
62
- readonly enum: [403];
62
+ readonly enum: [403, 404];
63
63
  };
64
64
  readonly code: {
65
- readonly enum: ["USER_NOT_ACTIVE"];
65
+ readonly enum: ["USER_NOT_ACTIVE", "USER_NOT_FOUND"];
66
66
  };
67
67
  readonly message: {
68
68
  readonly type: "string";
@@ -1,6 +1,7 @@
1
+ import { ObjectId } from 'mongodb';
1
2
  import { Result, HTTPStatus, resultSchema, functionSchemas, endpointErrorSchema, defineContract } from '@aeriajs/types';
2
3
  import { RedefinePasswordError } from './redefinePassword.js';
3
- import { getActivationToken } from './getActivationLink.js';
4
+ import { getActivationToken } from './getActivationToken.js';
4
5
  export const getRedefinePasswordLinkContract = defineContract({
5
6
  payload: {
6
7
  type: 'object',
@@ -18,8 +19,14 @@ export const getRedefinePasswordLinkContract = defineContract({
18
19
  response: [
19
20
  functionSchemas.getError(),
20
21
  endpointErrorSchema({
21
- httpStatus: [HTTPStatus.Forbidden],
22
- code: [RedefinePasswordError.UserNotActive],
22
+ httpStatus: [
23
+ HTTPStatus.Forbidden,
24
+ HTTPStatus.NotFound,
25
+ ],
26
+ code: [
27
+ RedefinePasswordError.UserNotActive,
28
+ RedefinePasswordError.UserNotFound,
29
+ ],
23
30
  }),
24
31
  resultSchema({
25
32
  type: 'object',
@@ -35,25 +42,40 @@ export const getRedefinePasswordLink = async (payload, context) => {
35
42
  if (!context.config.webPublicUrl) {
36
43
  throw new Error('config.webPublicUrl is not set');
37
44
  }
38
- const { error, result: user } = await context.collections.user.functions.get({
39
- filters: {
40
- _id: payload.userId,
45
+ console.log(payload);
46
+ const now = new Date();
47
+ const user = await context.collections.user.model.findOneAndUpdate({
48
+ _id: new ObjectId(payload.userId),
49
+ }, {
50
+ $set: {
51
+ activation_timestamp: now,
52
+ },
53
+ }, {
54
+ returnDocument: 'after',
55
+ projection: {
56
+ active: 1,
57
+ password: 1,
41
58
  },
42
- project: ['active'],
43
59
  });
44
- if (error) {
45
- return Result.error(error);
60
+ if (!user) {
61
+ return Result.error({
62
+ httpStatus: HTTPStatus.NotFound,
63
+ code: RedefinePasswordError.UserNotFound,
64
+ });
46
65
  }
47
66
  if (!user.active) {
48
67
  return context.error(HTTPStatus.Forbidden, {
49
68
  code: RedefinePasswordError.UserNotActive,
50
69
  });
51
70
  }
52
- const redefineToken = await getActivationToken(payload.userId.toString(), context);
71
+ const token = await getActivationToken({
72
+ _id: user._id,
73
+ timestamp: now,
74
+ }, context);
53
75
  const url = new URL(`${context.config.webPublicUrl}/user/redefine-password`);
54
76
  url.searchParams.set('step', 'password'),
55
77
  url.searchParams.set('u', payload.userId.toString());
56
- url.searchParams.set('t', redefineToken);
78
+ url.searchParams.set('t', token);
57
79
  if (payload.redirect) {
58
80
  url.searchParams.set('next', payload.redirect);
59
81
  }