@mindful-web/marko-web-identity-x 1.0.0
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/.eslintignore +1 -0
- package/LICENSE +21 -0
- package/README.md +200 -0
- package/api/fragment-types.json +1 -0
- package/api/fragments/active-user.js +108 -0
- package/api/mutations/add-external-user-id.js +24 -0
- package/api/mutations/create-app-user.js +11 -0
- package/api/mutations/delete-app-user-for-current-application.js +7 -0
- package/api/mutations/impersonate-app-user.js +12 -0
- package/api/mutations/logout-app-user.js +11 -0
- package/api/mutations/send-change-email-link.js +7 -0
- package/api/mutations/send-login-link.js +7 -0
- package/api/mutations/set-custom-attributes.js +15 -0
- package/api/queries/check-content-access.js +19 -0
- package/api/queries/get-active-context.js +45 -0
- package/api/queries/load-user-by-external-id.js +14 -0
- package/api/queries/load-user-by-id.js +11 -0
- package/api/queries/load-user.js +11 -0
- package/browser/.eslintrc.js +11 -0
- package/browser/access.vue +377 -0
- package/browser/authenticate.vue +278 -0
- package/browser/change-email-confirm.vue +120 -0
- package/browser/change-email-init.vue +179 -0
- package/browser/comments/create.vue +107 -0
- package/browser/comments/post.vue +151 -0
- package/browser/comments/stream.vue +319 -0
- package/browser/custom-column.vue +314 -0
- package/browser/dayjs.js +6 -0
- package/browser/download-related.vue +120 -0
- package/browser/download.vue +380 -0
- package/browser/errors/authentication.js +7 -0
- package/browser/errors/feature.js +7 -0
- package/browser/errors/form.js +7 -0
- package/browser/errors/logout.js +7 -0
- package/browser/form/address-block.vue +131 -0
- package/browser/form/common/checkbox-group.vue +140 -0
- package/browser/form/common/form-group.vue +24 -0
- package/browser/form/common/form-label.vue +17 -0
- package/browser/form/consent.vue +121 -0
- package/browser/form/fields/address-extra.vue +68 -0
- package/browser/form/fields/city.vue +68 -0
- package/browser/form/fields/comment-body.vue +63 -0
- package/browser/form/fields/country.vue +94 -0
- package/browser/form/fields/custom-boolean.vue +58 -0
- package/browser/form/fields/custom-select-group.vue +25 -0
- package/browser/form/fields/custom-select-multiple.vue +97 -0
- package/browser/form/fields/custom-select-write-in-inline.vue +39 -0
- package/browser/form/fields/custom-select-write-in.vue +66 -0
- package/browser/form/fields/custom-select.vue +191 -0
- package/browser/form/fields/custom-text.vue +62 -0
- package/browser/form/fields/display-name.vue +64 -0
- package/browser/form/fields/email.vue +62 -0
- package/browser/form/fields/family-name.vue +68 -0
- package/browser/form/fields/given-name.vue +68 -0
- package/browser/form/fields/organization-title.vue +68 -0
- package/browser/form/fields/organization.vue +68 -0
- package/browser/form/fields/phone-number.vue +68 -0
- package/browser/form/fields/postal-code.vue +68 -0
- package/browser/form/fields/receive-email.vue +58 -0
- package/browser/form/fields/region.vue +106 -0
- package/browser/form/fields/regional-policy.vue +59 -0
- package/browser/form/fields/street.vue +68 -0
- package/browser/graphql/create-client.js +65 -0
- package/browser/graphql/parse.js +1 -0
- package/browser/graphql/queries/download-related-content.js +38 -0
- package/browser/index.js +110 -0
- package/browser/login-fields.vue +214 -0
- package/browser/login.vue +331 -0
- package/browser/logout.vue +68 -0
- package/browser/mixins/global-auto-signup-event-emitter.js +18 -0
- package/browser/mixins/global-event-emitter.js +28 -0
- package/browser/profile.vue +688 -0
- package/browser/service.js +32 -0
- package/browser/utils/clean-path.js +1 -0
- package/browser/utils/cookies-enabled.js +10 -0
- package/browser/utils/get-as-array.js +6 -0
- package/browser/utils/get-referring-page.js +7 -0
- package/browser/utils/get.js +8 -0
- package/browser/utils/post.js +10 -0
- package/browser/utils/redirect.js +7 -0
- package/browser/utils/region-country-codes.js +1 -0
- package/components/access.marko +24 -0
- package/components/access.marko.js +61 -0
- package/components/comment-stream.marko +31 -0
- package/components/comment-stream.marko.js +71 -0
- package/components/context.marko +15 -0
- package/components/context.marko.js +52 -0
- package/components/form-access.marko +60 -0
- package/components/form-access.marko.js +111 -0
- package/components/form-authenticate.marko +32 -0
- package/components/form-authenticate.marko.js +73 -0
- package/components/form-change-email.marko +27 -0
- package/components/form-change-email.marko.js +68 -0
- package/components/form-login.marko +32 -0
- package/components/form-login.marko.js +72 -0
- package/components/form-logout.marko +7 -0
- package/components/form-logout.marko.js +36 -0
- package/components/form-profile.marko +38 -0
- package/components/form-profile.marko.js +78 -0
- package/components/form-register.marko +2 -0
- package/components/form-register.marko.js +32 -0
- package/components/identify.marko +17 -0
- package/components/identify.marko.js +59 -0
- package/components/marko.json +90 -0
- package/components/non-auth-identify.marko +15 -0
- package/components/non-auth-identify.marko.js +53 -0
- package/config.js +161 -0
- package/hooks.js +20 -0
- package/index.js +30 -0
- package/marko.json +6 -0
- package/middleware/content-access-state.js +31 -0
- package/middleware/content-download-state.js +27 -0
- package/middleware.js +32 -0
- package/package.json +40 -0
- package/routes/access.js +29 -0
- package/routes/authenticate.js +58 -0
- package/routes/change-email-confirm.js +60 -0
- package/routes/change-email-init.js +16 -0
- package/routes/comment-count.js +19 -0
- package/routes/comments.js +43 -0
- package/routes/countries.js +19 -0
- package/routes/create-comment.js +25 -0
- package/routes/download.js +29 -0
- package/routes/flag-comment.js +21 -0
- package/routes/index.js +40 -0
- package/routes/login-fields.js +35 -0
- package/routes/login.js +95 -0
- package/routes/logout.js +19 -0
- package/routes/profile.js +183 -0
- package/routes/regions.js +22 -0
- package/service.js +483 -0
- package/utils/call-hooks-for.js +28 -0
- package/utils/context-cookie.js +30 -0
- package/utils/create-client.js +48 -0
- package/utils/create-identity-builder.js +33 -0
- package/utils/get-as-array.js +6 -0
- package/utils/json-error-handler.js +41 -0
- package/utils/token-cookie.js +30 -0
package/config.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const { get, getAsArray, getAsObject } = require('@mindful-web/object-path');
|
|
2
|
+
const { isFunction: isFn } = require('@mindful-web/utils');
|
|
3
|
+
const validHooks = require('./hooks');
|
|
4
|
+
|
|
5
|
+
const { log } = console;
|
|
6
|
+
|
|
7
|
+
class IdentityXConfiguration {
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {object} options
|
|
11
|
+
* @param {string} options.appId The application ID to use.
|
|
12
|
+
* @param {string} [options.apiToken] An API token to use. Only required when doing write ops.
|
|
13
|
+
* @param {string[]} [options.requiredServerFields] Required fields, server enforced.
|
|
14
|
+
* @param {string[]} [options.requiredClientFields] Required fields, client-side only.
|
|
15
|
+
* @param {string[]} [options.requiredCreateFields] Required fields, when creating a new user.
|
|
16
|
+
* @param {string[]} [options.activeCustomFieldIds] Limit displayed custom fields, if present.
|
|
17
|
+
* @param {string[]} [options.hiddenFields] The fields to hide from the profile.
|
|
18
|
+
* @param {function} [options.onHookError]
|
|
19
|
+
* @param {object} options.rest
|
|
20
|
+
*/
|
|
21
|
+
constructor({
|
|
22
|
+
appId,
|
|
23
|
+
apiToken,
|
|
24
|
+
requiredServerFields = [],
|
|
25
|
+
requiredClientFields = [],
|
|
26
|
+
requiredCreateFields = [],
|
|
27
|
+
activeCustomFieldIds = [],
|
|
28
|
+
additionalCustomFieldIds = [],
|
|
29
|
+
defaultFieldLabels = {},
|
|
30
|
+
hiddenFields = ['city', 'street', 'addressExtra', 'phoneNumber', 'mobileNumber'],
|
|
31
|
+
defaultCountryCode,
|
|
32
|
+
booleanQuestionsLabel,
|
|
33
|
+
gtmUserFields = {},
|
|
34
|
+
onHookError,
|
|
35
|
+
...rest
|
|
36
|
+
} = {}) {
|
|
37
|
+
if (!appId) throw new Error('Unable to configure IdentityX: no Application ID was provided.');
|
|
38
|
+
this.appId = appId;
|
|
39
|
+
this.apiToken = apiToken;
|
|
40
|
+
this.options = {
|
|
41
|
+
enableChangeEmail: false,
|
|
42
|
+
requiredServerFields,
|
|
43
|
+
requiredClientFields,
|
|
44
|
+
requiredCreateFields,
|
|
45
|
+
activeCustomFieldIds,
|
|
46
|
+
additionalCustomFieldIds,
|
|
47
|
+
defaultFieldLabels,
|
|
48
|
+
hiddenFields,
|
|
49
|
+
defaultCountryCode,
|
|
50
|
+
booleanQuestionsLabel,
|
|
51
|
+
gtmUserFields,
|
|
52
|
+
onHookError: (e) => {
|
|
53
|
+
if (process.env.NODE_ENV === 'development') {
|
|
54
|
+
log('ERROR IN IDENTITY-X HOOK', e);
|
|
55
|
+
if (e.networkError) log('Network Error', get(e, 'networkError.result.errors.0'));
|
|
56
|
+
}
|
|
57
|
+
if (isFn(onHookError)) onHookError(e);
|
|
58
|
+
},
|
|
59
|
+
...rest,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
this.endpointTypes = [
|
|
63
|
+
'authenticate',
|
|
64
|
+
'changeEmail',
|
|
65
|
+
'login',
|
|
66
|
+
'logout',
|
|
67
|
+
'register',
|
|
68
|
+
'profile',
|
|
69
|
+
];
|
|
70
|
+
this.hooks = validHooks.reduce((o, name) => ({ ...o, [name]: [] }), {});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Adds a function to the hook queue.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} params
|
|
77
|
+
* @param {string} params.string The hook name to register the function with.
|
|
78
|
+
* @param {function} params.fn The function to call. Can be async/promise.
|
|
79
|
+
* @param {boolean} [params.shouldAwait=false] Whether the function should be awaited.
|
|
80
|
+
*/
|
|
81
|
+
addHook({ name, fn, shouldAwait = false } = {}) {
|
|
82
|
+
if (!validHooks.includes(name)) throw new Error(`No hook found for '${name}'`);
|
|
83
|
+
if (!isFn(fn)) throw new Error('The hook `fn` must be a function.');
|
|
84
|
+
this.hooks[name].push({ fn, shouldAwait });
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
commentsEnabled() {
|
|
89
|
+
return this.get('comments.enabled', true);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getAppId() {
|
|
93
|
+
return this.appId;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getApiToken() {
|
|
97
|
+
return this.apiToken;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getEndpointFor(type) {
|
|
101
|
+
return this.get(`endpoints.${type}`, `/user/${type}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getEndpoints() {
|
|
105
|
+
return this.endpointTypes.reduce((o, type) => {
|
|
106
|
+
const endpoint = this.getEndpointFor(type);
|
|
107
|
+
return { ...o, [type]: endpoint };
|
|
108
|
+
}, {});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getRequiredServerFields() {
|
|
112
|
+
return this.getAsArray('requiredServerFields');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getGTMUserData(user) {
|
|
116
|
+
if (!user) return {};
|
|
117
|
+
const questions = this.getAsObject('gtmUserFields');
|
|
118
|
+
const userData = { id: user.id };
|
|
119
|
+
Object.entries(questions).forEach(([key, value]) => {
|
|
120
|
+
const select = getAsArray(user, 'customSelectFieldAnswers').find(({ id }) => id === value);
|
|
121
|
+
if (select && select.answers.length) userData[key] = select.answers.map(({ label }) => label).join('|');
|
|
122
|
+
const boolean = getAsArray(user, 'customBooleanFieldAnswers').find(({ id }) => id === value);
|
|
123
|
+
if (boolean && boolean.hasAnswered) userData[key] = boolean.answer;
|
|
124
|
+
});
|
|
125
|
+
return userData;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getRequiredClientFields() {
|
|
129
|
+
return this.getAsArray('requiredClientFields');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getRequiredCreateFields() {
|
|
133
|
+
return this.getAsArray('requiredCreateFields');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getHiddenFields() {
|
|
137
|
+
return this.getAsArray('hiddenFields');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getActiveCustomFieldIds() {
|
|
141
|
+
return this.getAsArray('activeCustomFieldIds');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getAdditionalCustomFieldIds() {
|
|
145
|
+
return this.getAsArray('additionalCustomFieldIds');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get(path, def) {
|
|
149
|
+
return get(this.options, path, def);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getAsArray(path) {
|
|
153
|
+
return getAsArray(this.options, path);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getAsObject(path) {
|
|
157
|
+
return getAsObject(this.options, path);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = IdentityXConfiguration;
|
package/hooks.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {(
|
|
3
|
+
* "onAuthenticationSuccess"|
|
|
4
|
+
* "onChangeEmailLinkSent"|
|
|
5
|
+
* "onChangeEmailSuccess"|
|
|
6
|
+
* "onLoadActiveContext"|
|
|
7
|
+
* "onLoginLinkSent"|
|
|
8
|
+
* "onLogout"|
|
|
9
|
+
* "onUserProfileUpdate"
|
|
10
|
+
* )} HookTypeEnum
|
|
11
|
+
*/
|
|
12
|
+
module.exports = [
|
|
13
|
+
'onAuthenticationSuccess',
|
|
14
|
+
'onChangeEmailLinkSent',
|
|
15
|
+
'onChangeEmailSuccess',
|
|
16
|
+
'onLoadActiveContext',
|
|
17
|
+
'onLoginLinkSent',
|
|
18
|
+
'onLogout',
|
|
19
|
+
'onUserProfileUpdate',
|
|
20
|
+
];
|
package/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const routes = require('./routes');
|
|
2
|
+
const middleware = require('./middleware');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {object} app The express app instance
|
|
7
|
+
* @param {IdentityXConfiguration} config The IdentityX config
|
|
8
|
+
*/
|
|
9
|
+
module.exports = (app, config, {
|
|
10
|
+
templates = {},
|
|
11
|
+
} = {}) => {
|
|
12
|
+
app.use(middleware(config));
|
|
13
|
+
app.use('/__idx', routes);
|
|
14
|
+
|
|
15
|
+
config.endpointTypes.forEach((type) => {
|
|
16
|
+
const endpoint = config.getEndpointFor(type);
|
|
17
|
+
const template = templates[type];
|
|
18
|
+
if (!template) return;
|
|
19
|
+
|
|
20
|
+
app.get(endpoint, (req, res) => {
|
|
21
|
+
const { token } = req.identityX;
|
|
22
|
+
// Redirect to profile if a user token is already present (refresh after link click)
|
|
23
|
+
if (type === 'authenticate' && token) {
|
|
24
|
+
const redirectTo = req.query.redirectTo || config.getEndpointFor('profile');
|
|
25
|
+
res.redirect(302, redirectTo);
|
|
26
|
+
}
|
|
27
|
+
res.marko(template);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
package/marko.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { get, getAsObject } = require('@mindful-web/object-path');
|
|
2
|
+
|
|
3
|
+
const cookieNamePrefix = '__idx_form';
|
|
4
|
+
// default is one day
|
|
5
|
+
const maxAge = process.env.IDX_CONTENT_ACCESS_MAXAGE
|
|
6
|
+
? Number(process.env.IDX_CONTENT_ACCESS_MAXAGE)
|
|
7
|
+
: (24 * 60 * 60 * 1000);
|
|
8
|
+
|
|
9
|
+
const contentAccessState = ({ res, content }) => {
|
|
10
|
+
// Handle setting of contentAccessState Object
|
|
11
|
+
const { surveyType, surveyId } = getAsObject(content, 'gating');
|
|
12
|
+
const cookieName = `${cookieNamePrefix}_${surveyId}_${content.id}`;
|
|
13
|
+
|
|
14
|
+
const setFormDisplay = ({ req }) => {
|
|
15
|
+
res.locals.contentAccessState.displayForm = !get(req, `cookies.${cookieName}`);
|
|
16
|
+
};
|
|
17
|
+
res.locals.contentAccessState = {
|
|
18
|
+
displayForm: false,
|
|
19
|
+
setFormDisplay,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (surveyType === 'idx') {
|
|
23
|
+
const cookie = { name: cookieName, maxAge };
|
|
24
|
+
res.locals.contentAccessState.formId = surveyId;
|
|
25
|
+
res.locals.contentAccessState.cookie = cookie;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
contentAccessState,
|
|
31
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { get } = require('@mindful-web/object-path');
|
|
2
|
+
|
|
3
|
+
const cookieNamePrefix = '__idx_form';
|
|
4
|
+
const maxAge = process.env.IDX_CONTENT_DOWNLOAD_MAXAGE
|
|
5
|
+
? Number(process.env.IDX_CONTENT_DOWNLOAD_MAXAGE)
|
|
6
|
+
: (24 * 60 * 60 * 1000);
|
|
7
|
+
|
|
8
|
+
const contentDownloadState = ({ res, content }) => {
|
|
9
|
+
// Handle setting of contentDownloadState Object
|
|
10
|
+
const surveyId = 'content-download';
|
|
11
|
+
const cookieName = `${cookieNamePrefix}_${surveyId}_${content.id}`;
|
|
12
|
+
|
|
13
|
+
const setFormDisplay = ({ req }) => {
|
|
14
|
+
res.locals.contentDownloadState.displayForm = !get(req, `cookies.${cookieName}`);
|
|
15
|
+
};
|
|
16
|
+
res.locals.contentDownloadState = {
|
|
17
|
+
displayForm: false,
|
|
18
|
+
setFormDisplay,
|
|
19
|
+
};
|
|
20
|
+
const cookie = { name: cookieName, maxAge };
|
|
21
|
+
res.locals.contentDownloadState.formId = surveyId;
|
|
22
|
+
res.locals.contentDownloadState.cookie = cookie;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
contentDownloadState,
|
|
27
|
+
};
|
package/middleware.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
2
|
+
const IdentityX = require('./service');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An Express middleware that injects the IdentityX service.
|
|
6
|
+
*
|
|
7
|
+
* @param {IdentityXConfiguration} config The IdentityX config object
|
|
8
|
+
* @returns {function} The middleware function
|
|
9
|
+
*
|
|
10
|
+
* @typedef IdentityXRequest
|
|
11
|
+
* @prop {IdentityX} identityX The IdentityX service instance
|
|
12
|
+
*/
|
|
13
|
+
module.exports = (config) => asyncRoute(async (req, res, next) => {
|
|
14
|
+
const service = new IdentityX({ req, res, config });
|
|
15
|
+
req.identityX = service;
|
|
16
|
+
res.locals.identityX = service;
|
|
17
|
+
|
|
18
|
+
const cookie = service.getIdentity(res);
|
|
19
|
+
|
|
20
|
+
// Don't overwrite an existing cookie
|
|
21
|
+
if (cookie) return next();
|
|
22
|
+
|
|
23
|
+
// Set cookie for logged in users
|
|
24
|
+
if (service.token) {
|
|
25
|
+
const { user } = await service.loadActiveContext();
|
|
26
|
+
if (user && user.id) {
|
|
27
|
+
service.setIdentityCookie(user.id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return next();
|
|
32
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mindful-web/marko-web-identity-x",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Marko Wrapper for IdentityX",
|
|
5
|
+
"repository": "https://github.com/parameter1/mindful-web/tree/main/packages/marko-web-identity-x",
|
|
6
|
+
"author": "Josh Worden <josh@parameter1.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"lint:fix": "yarn lint --fix",
|
|
10
|
+
"lint": "eslint --ext .js --ext .vue --max-warnings 5 ./",
|
|
11
|
+
"compile": "mindful-web-marko-compile compile",
|
|
12
|
+
"prepublish": "yarn compile --silent",
|
|
13
|
+
"test": "yarn compile --no-clean && yarn lint"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@mindful-web/marko-web-gtm": "^1.0.0",
|
|
17
|
+
"@mindful-web/object-path": "^1.0.0",
|
|
18
|
+
"@mindful-web/utils": "^1.0.0",
|
|
19
|
+
"apollo-cache-inmemory": "^1.6.6",
|
|
20
|
+
"apollo-client": "^2.6.10",
|
|
21
|
+
"apollo-link-context": "^1.0.20",
|
|
22
|
+
"apollo-link-http": "^1.5.17",
|
|
23
|
+
"body-parser": "^1.20.1",
|
|
24
|
+
"cookie": "0.3.1",
|
|
25
|
+
"dayjs": "^1.11.7",
|
|
26
|
+
"express": "^4.18.2",
|
|
27
|
+
"graphql-tag": "^2.12.6",
|
|
28
|
+
"jsonwebtoken": "^8.5.1",
|
|
29
|
+
"node-fetch": "^2.6.9",
|
|
30
|
+
"object-path": "^0.11.8"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@mindful-web/marko-core": "^0.0.0",
|
|
34
|
+
"@mindful-web/marko-web": "^0.0.0"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "0b77cab713eb5841202bb86c7119949866bc68b5"
|
|
40
|
+
}
|
package/routes/access.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const mutation = gql`
|
|
5
|
+
mutation StoreContentAccessSubmission($input: CreateContentAccessSubmissionMutationInput!) {
|
|
6
|
+
createContentAccessSubmission(input:$input) {
|
|
7
|
+
id
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
13
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
|
+
const { body, apollo, identityX } = req;
|
|
15
|
+
const { contentId, payload, cookie } = body;
|
|
16
|
+
const input = {
|
|
17
|
+
contentId,
|
|
18
|
+
payload,
|
|
19
|
+
ipAddress: req.ip,
|
|
20
|
+
};
|
|
21
|
+
const { name: COOKIE_NAME, maxAge } = cookie;
|
|
22
|
+
await apollo.mutate({ mutation, variables: { input } });
|
|
23
|
+
res.cookie(COOKIE_NAME, true, { maxAge, httpOnly: false });
|
|
24
|
+
const entity = await identityX.generateEntityId();
|
|
25
|
+
res.json({
|
|
26
|
+
ok: true,
|
|
27
|
+
entity,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
const tokenCookie = require('../utils/token-cookie');
|
|
4
|
+
const contextCookie = require('../utils/context-cookie');
|
|
5
|
+
const callHooksFor = require('../utils/call-hooks-for');
|
|
6
|
+
const userFragment = require('../api/fragments/active-user');
|
|
7
|
+
|
|
8
|
+
const loginAppUser = gql`
|
|
9
|
+
mutation LoginAppUser($input: LoginAppUserMutationInput!) {
|
|
10
|
+
loginAppUser(input: $input) {
|
|
11
|
+
token {
|
|
12
|
+
id
|
|
13
|
+
value
|
|
14
|
+
}
|
|
15
|
+
user {
|
|
16
|
+
...ActiveUserFragment
|
|
17
|
+
}
|
|
18
|
+
loginSource
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
${userFragment}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
26
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
27
|
+
const { identityX, body } = req;
|
|
28
|
+
const { token, additionalEventData } = body;
|
|
29
|
+
if (!token) throw new Error('No login token was provided.');
|
|
30
|
+
|
|
31
|
+
const input = { token };
|
|
32
|
+
const variables = { input };
|
|
33
|
+
const { data = {} } = await identityX.client.mutate({ mutation: loginAppUser, variables });
|
|
34
|
+
const { token: authToken, user, loginSource } = data.loginAppUser;
|
|
35
|
+
|
|
36
|
+
// call authentication hooks
|
|
37
|
+
await callHooksFor(identityX, 'onAuthenticationSuccess', {
|
|
38
|
+
req,
|
|
39
|
+
res,
|
|
40
|
+
user,
|
|
41
|
+
authToken,
|
|
42
|
+
loginSource,
|
|
43
|
+
additionalEventData,
|
|
44
|
+
});
|
|
45
|
+
tokenCookie.setTo(res, authToken.value);
|
|
46
|
+
contextCookie.setTo(res, { loginSource });
|
|
47
|
+
identityX.setIdentityCookie(user.id);
|
|
48
|
+
const entity = await identityX.generateEntityId({ userId: user.id });
|
|
49
|
+
const refreshed = await identityX.findUserById(user.id);
|
|
50
|
+
res.json({
|
|
51
|
+
ok: true,
|
|
52
|
+
applicationId: identityX.config.getAppId(),
|
|
53
|
+
user: refreshed,
|
|
54
|
+
loginSource,
|
|
55
|
+
additionalEventData,
|
|
56
|
+
entity,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const jwt = require('jsonwebtoken');
|
|
3
|
+
const { get } = require('@mindful-web/object-path');
|
|
4
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
5
|
+
const tokenCookie = require('../utils/token-cookie');
|
|
6
|
+
const contextCookie = require('../utils/context-cookie');
|
|
7
|
+
const callHooksFor = require('../utils/call-hooks-for');
|
|
8
|
+
const userFragment = require('../api/fragments/active-user');
|
|
9
|
+
|
|
10
|
+
const mutation = gql`
|
|
11
|
+
mutation ChangeEmailConfirm($input: ChangeAppUserEmailMutationInput!) {
|
|
12
|
+
changeAppUserEmail(input: $input) {
|
|
13
|
+
token {
|
|
14
|
+
id
|
|
15
|
+
value
|
|
16
|
+
}
|
|
17
|
+
user {
|
|
18
|
+
...ActiveUserFragment
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
${userFragment}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
27
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
28
|
+
const { identityX, body } = req;
|
|
29
|
+
const { token } = body;
|
|
30
|
+
const loginSource = 'change-email';
|
|
31
|
+
if (!token) throw new Error('No login token was provided.');
|
|
32
|
+
|
|
33
|
+
const input = { token };
|
|
34
|
+
const variables = { input };
|
|
35
|
+
const { data = {} } = await identityX.client.mutate({ mutation, variables });
|
|
36
|
+
const { token: authToken, user } = data.changeAppUserEmail;
|
|
37
|
+
|
|
38
|
+
const decoded = await jwt.decode(token);
|
|
39
|
+
await callHooksFor(identityX, 'onChangeEmailSuccess', {
|
|
40
|
+
user,
|
|
41
|
+
oldEmail: get(decoded, 'data.email'),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// call authentication hooks
|
|
45
|
+
await callHooksFor(identityX, 'onAuthenticationSuccess', {
|
|
46
|
+
req,
|
|
47
|
+
res,
|
|
48
|
+
user,
|
|
49
|
+
authToken,
|
|
50
|
+
loginSource,
|
|
51
|
+
});
|
|
52
|
+
tokenCookie.setTo(res, authToken.value);
|
|
53
|
+
contextCookie.setTo(res, { loginSource });
|
|
54
|
+
const entity = await identityX.generateEntityId({ userId: user.id });
|
|
55
|
+
res.json({
|
|
56
|
+
ok: true,
|
|
57
|
+
user,
|
|
58
|
+
entity,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
2
|
+
|
|
3
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
4
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
5
|
+
const { identityX, body } = req;
|
|
6
|
+
const { email } = body;
|
|
7
|
+
const entity = await identityX.generateEntityId();
|
|
8
|
+
|
|
9
|
+
// Send login link.
|
|
10
|
+
await identityX.sendChangeEmailLink({ email });
|
|
11
|
+
await identityX.logoutAppUser();
|
|
12
|
+
return res.json({
|
|
13
|
+
ok: true,
|
|
14
|
+
entity,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const query = gql`
|
|
5
|
+
query CountComments($identifier: String!) {
|
|
6
|
+
commentsForStream(input: { identifier: $identifier }) {
|
|
7
|
+
totalCount
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
13
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
|
+
const { identityX } = req;
|
|
15
|
+
const { identifier } = req.params;
|
|
16
|
+
const variables = { identifier };
|
|
17
|
+
const { data } = await identityX.client.query({ query, variables });
|
|
18
|
+
res.json(data.commentsForStream);
|
|
19
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const query = gql`
|
|
5
|
+
query ListComments($input: CommentsForStreamQueryInput!) {
|
|
6
|
+
commentsForStream(input: $input) {
|
|
7
|
+
totalCount
|
|
8
|
+
stream {
|
|
9
|
+
id
|
|
10
|
+
archived
|
|
11
|
+
}
|
|
12
|
+
pageInfo {
|
|
13
|
+
endCursor
|
|
14
|
+
hasNextPage
|
|
15
|
+
}
|
|
16
|
+
edges {
|
|
17
|
+
node {
|
|
18
|
+
id
|
|
19
|
+
body
|
|
20
|
+
approved
|
|
21
|
+
flagged
|
|
22
|
+
createdAt
|
|
23
|
+
user {
|
|
24
|
+
id
|
|
25
|
+
displayName
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
34
|
+
const { identifier } = req.params;
|
|
35
|
+
const { after } = req.query;
|
|
36
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
37
|
+
const { identityX } = req;
|
|
38
|
+
const limit = after ? 20 : 5;
|
|
39
|
+
const pagination = { limit, after };
|
|
40
|
+
const variables = { input: { identifier, pagination } };
|
|
41
|
+
const { data } = await identityX.client.query({ query, variables });
|
|
42
|
+
res.json(data.commentsForStream);
|
|
43
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const localeCountries = gql`
|
|
5
|
+
query LocaleCountries {
|
|
6
|
+
localeCountries {
|
|
7
|
+
id
|
|
8
|
+
name
|
|
9
|
+
flag
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
15
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
16
|
+
const { identityX } = req;
|
|
17
|
+
const { data } = await identityX.client.query({ query: localeCountries });
|
|
18
|
+
res.json(data.localeCountries);
|
|
19
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const mutation = gql`
|
|
5
|
+
mutation CreateComment($input: CreateCommentMutationInput!) {
|
|
6
|
+
createComment(input: $input) {
|
|
7
|
+
id
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
13
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
|
+
const { identityX } = req;
|
|
15
|
+
const { displayName, body, stream } = req.body;
|
|
16
|
+
|
|
17
|
+
const input = { displayName, body, stream };
|
|
18
|
+
const variables = { input };
|
|
19
|
+
await identityX.client.mutate({ mutation, variables });
|
|
20
|
+
const entity = await identityX.generateEntityId();
|
|
21
|
+
res.json({
|
|
22
|
+
ok: true,
|
|
23
|
+
entity,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const mutation = gql`
|
|
5
|
+
mutation StoreContentDownloadSubmission($input: CreateContentDownloadSubmissionMutationInput!) {
|
|
6
|
+
createContentDownloadSubmission(input:$input) {
|
|
7
|
+
id
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
13
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
|
+
const { body, apollo, identityX } = req;
|
|
15
|
+
const { contentId, payload, cookie } = body;
|
|
16
|
+
const input = {
|
|
17
|
+
contentId,
|
|
18
|
+
payload,
|
|
19
|
+
ipAddress: req.ip,
|
|
20
|
+
};
|
|
21
|
+
const { name: COOKIE_NAME, maxAge } = cookie;
|
|
22
|
+
await apollo.mutate({ mutation, variables: { input } });
|
|
23
|
+
res.cookie(COOKIE_NAME, true, { maxAge, httpOnly: false });
|
|
24
|
+
const entity = await identityX.generateEntityId();
|
|
25
|
+
res.json({
|
|
26
|
+
ok: true,
|
|
27
|
+
entity,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const gql = require('graphql-tag');
|
|
2
|
+
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
|
|
4
|
+
const mutation = gql`
|
|
5
|
+
mutation FlagComment($id: String!) {
|
|
6
|
+
setCommentFlagged(input: { id: $id, value: true }) {
|
|
7
|
+
id
|
|
8
|
+
flagged
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
module.exports = asyncRoute(async (req, res) => {
|
|
14
|
+
/** @type {import('../middleware').IdentityXRequest} */
|
|
15
|
+
const { identityX } = req;
|
|
16
|
+
const { id } = req.params;
|
|
17
|
+
const variables = { id };
|
|
18
|
+
await identityX.client.mutate({ mutation, variables });
|
|
19
|
+
const entity = await identityX.generateEntityId();
|
|
20
|
+
res.json({ ok: true, entity });
|
|
21
|
+
});
|