backend-manager 5.0.75 → 5.0.77
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/package.json +1 -2
- package/src/manager/cron/daily/ghostii-auto-publisher.js +44 -26
- package/src/manager/functions/core/actions/api/firebase/get-providers.js +2 -46
- package/src/manager/functions/core/actions/api/general/send-email.js +1 -1
- package/src/manager/functions/core/actions/api/special/setup-electron-manager-client.js +14 -29
- package/src/manager/functions/core/actions/api/user/submit-feedback.js +30 -44
- package/src/manager/functions/core/actions/api.js +1 -1
- package/src/manager/helpers/analytics.js +10 -10
- package/src/manager/helpers/api-manager.js +1 -1
- package/src/manager/helpers/assistant.js +32 -64
- package/src/manager/helpers/middleware.js +2 -1
- package/src/manager/helpers/usage.js +1 -1
- package/src/manager/helpers/user.js +23 -23
- package/src/manager/index.js +0 -31
- package/src/manager/libraries/openai.js +1 -1
- package/src/manager/routes/app/get.js +41 -0
- package/src/manager/routes/firebase/providers/get.js +2 -32
- package/src/manager/routes/general/email/post.js +1 -1
- package/src/manager/routes/special/electron-client/post.js +4 -18
- package/src/manager/routes/user/feedback/post.js +2 -12
- package/src/manager/schemas/app/get.js +1 -0
- package/src/test/test-accounts.js +2 -2
- package/templates/backend-manager-config.json +11 -0
- package/test/routes/user/user.js +7 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.77",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -74,7 +74,6 @@
|
|
|
74
74
|
"npm-api": "^1.0.1",
|
|
75
75
|
"paypal-server-api": "^2.0.14",
|
|
76
76
|
"pushid": "^1.0.0",
|
|
77
|
-
"resolve-account": "^1.0.26",
|
|
78
77
|
"shortid": "^2.2.17",
|
|
79
78
|
"uid-generator": "^2.0.0",
|
|
80
79
|
"uuid": "^9.0.1",
|
|
@@ -16,7 +16,6 @@ const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
|
|
|
16
16
|
|
|
17
17
|
// State
|
|
18
18
|
let postId;
|
|
19
|
-
let appObject;
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Ghostii Auto Publisher cron job
|
|
@@ -30,11 +29,8 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
|
|
|
30
29
|
// Set post ID
|
|
31
30
|
postId = moment().unix();
|
|
32
31
|
|
|
33
|
-
//
|
|
34
|
-
appObject =
|
|
35
|
-
if (appObject instanceof Error) {
|
|
36
|
-
throw appObject;
|
|
37
|
-
}
|
|
32
|
+
// Build app object from local config
|
|
33
|
+
const appObject = buildAppObject(Manager.config);
|
|
38
34
|
|
|
39
35
|
// Log
|
|
40
36
|
assistant.log('App object', appObject);
|
|
@@ -44,8 +40,6 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
|
|
|
44
40
|
|
|
45
41
|
// Loop through each item
|
|
46
42
|
for (const settings of settingsArray) {
|
|
47
|
-
const appId = settings.app || appObject.id;
|
|
48
|
-
|
|
49
43
|
// Fix settings
|
|
50
44
|
settings.articles = settings.articles || 0;
|
|
51
45
|
settings.sources = randomize(settings.sources || []);
|
|
@@ -53,16 +47,23 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
|
|
|
53
47
|
settings.prompt = settings.prompt || '';
|
|
54
48
|
settings.chance = settings.chance || 1.0;
|
|
55
49
|
settings.author = settings.author || undefined;
|
|
56
|
-
settings.app = await getAppData(appId).catch((e) => e);
|
|
57
50
|
|
|
58
|
-
//
|
|
59
|
-
if (settings.app
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
// Resolve app data for this ghostii item
|
|
52
|
+
if (settings.app && settings.appUrl) {
|
|
53
|
+
// Cross-app: fetch from the other project's /app endpoint
|
|
54
|
+
settings.app = await fetchRemoteApp(settings.appUrl).catch((e) => e);
|
|
55
|
+
|
|
56
|
+
if (settings.app instanceof Error) {
|
|
57
|
+
assistant.error('Error fetching remote app data', settings.app);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Same-app: use local config
|
|
62
|
+
settings.app = appObject;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
// Log
|
|
65
|
-
assistant.log(`Settings (app=${
|
|
66
|
+
assistant.log(`Settings (app=${settings.app.id})`, settings);
|
|
66
67
|
|
|
67
68
|
// Quit if articles are disabled
|
|
68
69
|
if (!settings.articles || !settings.sources.length) {
|
|
@@ -88,6 +89,35 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
|
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Build app object from Manager.config (same shape as getApp response)
|
|
94
|
+
*/
|
|
95
|
+
function buildAppObject(config) {
|
|
96
|
+
return {
|
|
97
|
+
id: config.app?.id,
|
|
98
|
+
name: config.brand?.name,
|
|
99
|
+
brand: {
|
|
100
|
+
description: config.brand?.description || '',
|
|
101
|
+
},
|
|
102
|
+
url: config.brand?.url,
|
|
103
|
+
github: {
|
|
104
|
+
user: config.github?.user,
|
|
105
|
+
repo: (config.github?.repo_website || '').split('/').pop(),
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Fetch app data from a remote BEM project's /app endpoint
|
|
112
|
+
*/
|
|
113
|
+
function fetchRemoteApp(appUrl) {
|
|
114
|
+
return fetch(`${appUrl}/backend-manager/app`, {
|
|
115
|
+
timeout: 30000,
|
|
116
|
+
tries: 3,
|
|
117
|
+
response: 'json',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
91
121
|
async function harvest(assistant, settings) {
|
|
92
122
|
const date = moment().format('MMMM YYYY');
|
|
93
123
|
|
|
@@ -151,18 +181,6 @@ async function harvest(assistant, settings) {
|
|
|
151
181
|
}
|
|
152
182
|
}
|
|
153
183
|
|
|
154
|
-
function getAppData(id) {
|
|
155
|
-
return fetch('https://us-central1-itw-creative-works.cloudfunctions.net/getApp', {
|
|
156
|
-
method: 'post',
|
|
157
|
-
timeout: 30000,
|
|
158
|
-
tries: 3,
|
|
159
|
-
response: 'json',
|
|
160
|
-
body: {
|
|
161
|
-
id: id,
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
184
|
function getURLContent(url) {
|
|
167
185
|
return fetch(url, {
|
|
168
186
|
timeout: 30000,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const fetch = require('wonderful-fetch');
|
|
2
1
|
const { merge } = require('lodash');
|
|
3
2
|
|
|
4
3
|
function Module() {
|
|
@@ -8,9 +7,7 @@ function Module() {
|
|
|
8
7
|
Module.prototype.main = function () {
|
|
9
8
|
const self = this;
|
|
10
9
|
const Manager = self.Manager;
|
|
11
|
-
const Api = self.Api;
|
|
12
10
|
const assistant = self.assistant;
|
|
13
|
-
const payload = self.payload;
|
|
14
11
|
|
|
15
12
|
return new Promise(async function(resolve, reject) {
|
|
16
13
|
const defaultProviders = {
|
|
@@ -40,14 +37,8 @@ Module.prototype.main = function () {
|
|
|
40
37
|
},
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
//
|
|
44
|
-
const
|
|
45
|
-
if (appObject instanceof Error) {
|
|
46
|
-
return reject(assistant.errorify(`Failed to get app object: ${appObject}`, {code: 500}));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Merge the default providers with the app providers
|
|
50
|
-
const providers = merge(defaultProviders, appObject.authentication);
|
|
40
|
+
// Merge the default providers with the config providers
|
|
41
|
+
const providers = merge(defaultProviders, Manager.config.authentication || {});
|
|
51
42
|
|
|
52
43
|
// Reformat the object so it's just provider=true/false
|
|
53
44
|
const finalProviders = {};
|
|
@@ -64,39 +55,4 @@ Module.prototype.main = function () {
|
|
|
64
55
|
|
|
65
56
|
};
|
|
66
57
|
|
|
67
|
-
// Get app object
|
|
68
|
-
Module.prototype.getAppObject = function () {
|
|
69
|
-
const self = this;
|
|
70
|
-
|
|
71
|
-
const Manager = self.Manager;
|
|
72
|
-
const Api = self.Api;
|
|
73
|
-
const assistant = self.assistant;
|
|
74
|
-
const payload = self.payload;
|
|
75
|
-
|
|
76
|
-
return new Promise(async function(resolve, reject) {
|
|
77
|
-
const id = Manager.config.app.id;
|
|
78
|
-
|
|
79
|
-
// Get the app settings
|
|
80
|
-
fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/getApp`, {
|
|
81
|
-
method: 'post',
|
|
82
|
-
response: 'json',
|
|
83
|
-
body: {
|
|
84
|
-
id: id,
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
.then((r) => {
|
|
88
|
-
assistant.log('getAppObject(): Response', r);
|
|
89
|
-
|
|
90
|
-
// If data is missing, return an error
|
|
91
|
-
if (!r) {
|
|
92
|
-
throw new Error(`App with id ${id} not found`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Return the app object
|
|
96
|
-
return resolve(r);
|
|
97
|
-
})
|
|
98
|
-
.catch(e => reject(e));
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
|
|
102
58
|
module.exports = Module;
|
|
@@ -49,7 +49,7 @@ Module.prototype.main = function () {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const storage = Manager.storage({temporary: true});
|
|
52
|
-
const ipPath = ['api:general:send-email', 'ips', assistant.request.geolocation.ip];
|
|
52
|
+
const ipPath = ['api:general:send-email', 'ips', assistant.request.geolocation.ip || 'unknown'];
|
|
53
53
|
const emailPath = ['api:general:send-email', 'emails', payload.data.payload.email];
|
|
54
54
|
|
|
55
55
|
const ipData = storage.get(ipPath).value() || {};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { buildPublicConfig } = require(path.join(__dirname, '..', '..', '..', '..', '..', 'routes', 'app', 'get.js'));
|
|
3
|
+
|
|
1
4
|
function Module() {
|
|
2
5
|
|
|
3
6
|
}
|
|
@@ -11,10 +14,7 @@ Module.prototype.main = function () {
|
|
|
11
14
|
|
|
12
15
|
return new Promise(async function(resolve, reject) {
|
|
13
16
|
|
|
14
|
-
const fetch = Manager.require('wonderful-fetch');
|
|
15
|
-
|
|
16
17
|
let uid = payload.data.payload.uid;
|
|
17
|
-
const app = payload.data.payload.appId || payload.data.payload.app || Manager.config.app.id;
|
|
18
18
|
let config = payload.data.payload.config || {};
|
|
19
19
|
|
|
20
20
|
let uuid = null;
|
|
@@ -68,32 +68,17 @@ Module.prototype.main = function () {
|
|
|
68
68
|
config = {};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return resolve({
|
|
83
|
-
data: {
|
|
84
|
-
uuid: uuid,
|
|
85
|
-
signInToken: signInToken,
|
|
86
|
-
timestamp: new Date().toISOString(),
|
|
87
|
-
ip: assistant.request.geolocation.ip,
|
|
88
|
-
country: assistant.request.geolocation.country,
|
|
89
|
-
app: result,
|
|
90
|
-
config: config,
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
})
|
|
94
|
-
.catch(e => {
|
|
95
|
-
return reject(assistant.errorify(`Error fetching app details: ${e}`, {code: 500}));
|
|
96
|
-
})
|
|
71
|
+
return resolve({
|
|
72
|
+
data: {
|
|
73
|
+
uuid: uuid,
|
|
74
|
+
signInToken: signInToken,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
ip: assistant.request.geolocation.ip,
|
|
77
|
+
country: assistant.request.geolocation.country,
|
|
78
|
+
app: buildPublicConfig(Manager.config),
|
|
79
|
+
config: config,
|
|
80
|
+
}
|
|
81
|
+
});
|
|
97
82
|
|
|
98
83
|
});
|
|
99
84
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const pushid = require('pushid');
|
|
2
|
-
const fetch = require('wonderful-fetch');
|
|
3
2
|
const powertools = require('node-powertools');
|
|
4
3
|
|
|
5
4
|
function Module() {
|
|
@@ -34,55 +33,42 @@ Module.prototype.main = function () {
|
|
|
34
33
|
decision.promptReview = true;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
// Get
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
body: {
|
|
42
|
-
id: Manager.config.app.id,
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
.then((response) => {
|
|
46
|
-
response.reviews = response.reviews || {};
|
|
47
|
-
response.reviews.enabled = typeof response.reviews.enabled === 'undefined' ? true : response.reviews.enabled;
|
|
48
|
-
response.reviews.sites = response.reviews.sites || [];
|
|
36
|
+
// Get review config from local config
|
|
37
|
+
const reviews = { ...(Manager.config.reviews || {}) };
|
|
38
|
+
reviews.enabled = typeof reviews.enabled === 'undefined' ? true : reviews.enabled;
|
|
39
|
+
reviews.sites = reviews.sites || [];
|
|
49
40
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
// If reviews are enabled and there are review sites, prompt review
|
|
42
|
+
if (decision.promptReview && reviews.enabled && reviews.sites.length > 0) {
|
|
43
|
+
decision.reviewURL = powertools.random(reviews.sites);
|
|
44
|
+
} else {
|
|
45
|
+
decision.promptReview = false;
|
|
46
|
+
}
|
|
56
47
|
|
|
57
|
-
|
|
48
|
+
assistant.log('Feedback submitted', docId, {appReviewData: reviews, request: request, decision: decision});
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})
|
|
78
|
-
.catch((e) => {
|
|
79
|
-
return reject(assistant.errorify(`Failed to save feedback: ${e.message}`, {code: 500, sentry: true}));
|
|
80
|
-
})
|
|
50
|
+
// Save feedback to firestore
|
|
51
|
+
self.libraries.admin.firestore().doc(`feedback/${docId}`)
|
|
52
|
+
.set({
|
|
53
|
+
created: assistant.meta.startTime,
|
|
54
|
+
feedback: request,
|
|
55
|
+
decision: decision,
|
|
56
|
+
owner: {
|
|
57
|
+
uid: user?.auth?.uid ?? null,
|
|
58
|
+
},
|
|
59
|
+
metadata: Manager.Metadata().set({tag: 'user:submit-feedback'}),
|
|
60
|
+
}, {merge: true})
|
|
61
|
+
.then(r => {
|
|
62
|
+
return resolve({
|
|
63
|
+
data: {
|
|
64
|
+
review: decision,
|
|
65
|
+
originalRequest: request,
|
|
66
|
+
}
|
|
67
|
+
});
|
|
81
68
|
})
|
|
82
69
|
.catch((e) => {
|
|
83
|
-
return reject(assistant.errorify(`Failed to
|
|
70
|
+
return reject(assistant.errorify(`Failed to save feedback: ${e.message}`, {code: 500, sentry: true}));
|
|
84
71
|
})
|
|
85
|
-
|
|
86
72
|
})
|
|
87
73
|
.catch((e) => {
|
|
88
74
|
return reject(e);
|
|
@@ -82,7 +82,7 @@ Module.prototype.main = function() {
|
|
|
82
82
|
// Log
|
|
83
83
|
// assistant.log(`Executing: ${resolved.command}`, self.payload, JSON.stringify(self.payload))
|
|
84
84
|
// assistant.log(`Resolved URL: ${Manager.project.functionsUrl}?command=${encodeURIComponent(resolved.command)}&payload=${encodeURIComponent(JSON.stringify(self.assistant.request.data.payload))}`)
|
|
85
|
-
assistant.log(`bm_api(${resolved.command}): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city}) [${method} > ${strippedUrl}]`, JSON.stringify(assistant.request.data));
|
|
85
|
+
assistant.log(`bm_api(${resolved.command}): Request (${geolocation.ip || 'unknown'} @ ${geolocation.country || '?'}, ${geolocation.region || '?'}, ${geolocation.city || '?'}) [${method} > ${strippedUrl}]`, JSON.stringify(assistant.request.data));
|
|
86
86
|
assistant.log(`bm_api(${resolved.command}): Headers`, JSON.stringify(headers));
|
|
87
87
|
|
|
88
88
|
|
|
@@ -23,21 +23,21 @@ function Analytics(Manager, options) {
|
|
|
23
23
|
|
|
24
24
|
// Set request properties
|
|
25
25
|
self.request = {
|
|
26
|
-
ip: self.assistant?.request?.geolocation?.ip ||
|
|
27
|
-
country: self.assistant?.request?.geolocation?.country ||
|
|
28
|
-
city: self.assistant?.request?.geolocation?.city ||
|
|
29
|
-
region: self.assistant?.request?.geolocation?.region ||
|
|
30
|
-
referrer: self.assistant?.request?.referrer ||
|
|
31
|
-
userAgent: self.assistant?.request?.client?.userAgent ||
|
|
32
|
-
language: (self.assistant?.request?.client?.language || '').split(',')[0],
|
|
26
|
+
ip: self.assistant?.request?.geolocation?.ip || null,
|
|
27
|
+
country: self.assistant?.request?.geolocation?.country || null,
|
|
28
|
+
city: self.assistant?.request?.geolocation?.city || null,
|
|
29
|
+
region: self.assistant?.request?.geolocation?.region || null,
|
|
30
|
+
referrer: self.assistant?.request?.referrer || null,
|
|
31
|
+
userAgent: self.assistant?.request?.client?.userAgent || null,
|
|
32
|
+
language: (self.assistant?.request?.client?.language || '').split(',')[0] || null,
|
|
33
33
|
mobile: self.assistant?.request?.client?.mobile || false,
|
|
34
|
-
platform: self.assistant?.request?.client?.platform ||
|
|
34
|
+
platform: self.assistant?.request?.client?.platform || null,
|
|
35
35
|
name: self.assistant?.meta?.name || '',
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// Remove blacklisted user agents
|
|
39
|
-
self.request.userAgent = BLOCKED_USER_AGENTS.some((regex) => self.request.userAgent.match(regex))
|
|
40
|
-
?
|
|
39
|
+
self.request.userAgent = self.request.userAgent && BLOCKED_USER_AGENTS.some((regex) => self.request.userAgent.match(regex))
|
|
40
|
+
? null
|
|
41
41
|
: self.request.userAgent;
|
|
42
42
|
|
|
43
43
|
// Fix options
|
|
@@ -170,7 +170,7 @@ ApiManager.prototype.getUser = async function (assistant) {
|
|
|
170
170
|
// console.log('---authenticatedUser', authenticatedUser);
|
|
171
171
|
const planId = authenticatedUser?.subscription?.product?.id || 'basic';
|
|
172
172
|
let workingUID = !authenticatedUser.authenticated
|
|
173
|
-
? uuidv5(assistant.request.geolocation.ip, '1b671a64-40d5-491e-99b0-da01ff1f3341')
|
|
173
|
+
? uuidv5(assistant.request.geolocation.ip || 'unknown', '1b671a64-40d5-491e-99b0-da01ff1f3341')
|
|
174
174
|
: authenticatedUser.auth.uid
|
|
175
175
|
authenticatedUser.ip = assistant.request.geolocation.ip;
|
|
176
176
|
authenticatedUser.country = assistant.request.geolocation.country;
|
|
@@ -221,7 +221,8 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
221
221
|
}
|
|
222
222
|
self.request.url = tryUrl(self);
|
|
223
223
|
self.request.path = self.ref.req.path || '';
|
|
224
|
-
self.request.user = self.
|
|
224
|
+
self.request.user = self.Manager.User({}).properties;
|
|
225
|
+
self.request.user.authenticated = false;
|
|
225
226
|
|
|
226
227
|
// Set body and query
|
|
227
228
|
if (options.accept === 'json') {
|
|
@@ -656,7 +657,8 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
656
657
|
|
|
657
658
|
// Resolve the user
|
|
658
659
|
if (options.resolve) {
|
|
659
|
-
self.request.user = self.
|
|
660
|
+
self.request.user = self.Manager.User(user).properties;
|
|
661
|
+
self.request.user.authenticated = user.authenticated || false;
|
|
660
662
|
return self.request.user;
|
|
661
663
|
} else {
|
|
662
664
|
return user;
|
|
@@ -763,11 +765,6 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
763
765
|
}
|
|
764
766
|
};
|
|
765
767
|
|
|
766
|
-
BackendAssistant.prototype.resolveAccount = function (user) {
|
|
767
|
-
const ResolveAccount = new (require('resolve-account'))();
|
|
768
|
-
|
|
769
|
-
return ResolveAccount.resolve(undefined, user)
|
|
770
|
-
}
|
|
771
768
|
|
|
772
769
|
BackendAssistant.prototype.parseRepo = function (repo) {
|
|
773
770
|
let repoSplit = repo.split('/');
|
|
@@ -795,43 +792,35 @@ BackendAssistant.prototype.parseRepo = function (repo) {
|
|
|
795
792
|
BackendAssistant.prototype.getHeaderIp = function (headers) {
|
|
796
793
|
headers = headers || {};
|
|
797
794
|
|
|
798
|
-
|
|
795
|
+
const value =
|
|
799
796
|
// these are present for cloudflare requests (11/21/2020)
|
|
800
797
|
headers['cf-connecting-ip']
|
|
801
798
|
|| headers['fastly-temp-xff']
|
|
802
799
|
|
|
803
800
|
// these are present for non-cloudflare requests (11/21/2020)
|
|
804
801
|
|| headers['x-appengine-user-ip']
|
|
805
|
-
|| headers['x-forwarded-for']
|
|
802
|
+
|| headers['x-forwarded-for'];
|
|
806
803
|
|
|
807
804
|
// Not sure about these
|
|
808
805
|
// || headers['fastly-client-ip']
|
|
809
806
|
|
|
810
|
-
|
|
811
|
-
|| '127.0.0.1'
|
|
812
|
-
)
|
|
813
|
-
.split(',')[0]
|
|
814
|
-
.trim();
|
|
807
|
+
return value ? value.split(',')[0].trim() : null;
|
|
815
808
|
}
|
|
816
809
|
|
|
817
810
|
BackendAssistant.prototype.getHeaderContinent = function (headers) {
|
|
818
811
|
headers = headers || {};
|
|
819
812
|
|
|
820
|
-
|
|
813
|
+
const value =
|
|
821
814
|
// these are present for cloudflare requests (11/21/2020)
|
|
822
|
-
headers['cf-ipcontinent']
|
|
815
|
+
headers['cf-ipcontinent'];
|
|
823
816
|
|
|
824
|
-
|
|
825
|
-
|| 'ZZ'
|
|
826
|
-
)
|
|
827
|
-
.split(',')[0]
|
|
828
|
-
.trim();
|
|
817
|
+
return value ? value.split(',')[0].trim() : null;
|
|
829
818
|
}
|
|
830
819
|
|
|
831
820
|
BackendAssistant.prototype.getHeaderCountry = function (headers) {
|
|
832
821
|
headers = headers || {};
|
|
833
822
|
|
|
834
|
-
|
|
823
|
+
const value =
|
|
835
824
|
// these are present for cloudflare requests (11/21/2020)
|
|
836
825
|
headers['cf-ipcountry']
|
|
837
826
|
|
|
@@ -839,46 +828,34 @@ BackendAssistant.prototype.getHeaderCountry = function (headers) {
|
|
|
839
828
|
|| headers['x-country-code']
|
|
840
829
|
|
|
841
830
|
// these are present for non-cloudflare requests (11/21/2020)
|
|
842
|
-
|| headers['x-appengine-country']
|
|
831
|
+
|| headers['x-appengine-country'];
|
|
843
832
|
|
|
844
|
-
|
|
845
|
-
|| 'ZZ'
|
|
846
|
-
)
|
|
847
|
-
.split(',')[0]
|
|
848
|
-
.trim();
|
|
833
|
+
return value ? value.split(',')[0].trim() : null;
|
|
849
834
|
}
|
|
850
835
|
|
|
851
836
|
BackendAssistant.prototype.getHeaderRegion = function (headers) {
|
|
852
837
|
headers = headers || {};
|
|
853
838
|
|
|
854
|
-
|
|
839
|
+
const value =
|
|
855
840
|
// these are present for cloudflare requests (11/21/2020)
|
|
856
841
|
headers['cf-region']
|
|
857
842
|
|
|
858
843
|
// these are present for non-cloudflare requests (11/21/2020)
|
|
859
|
-
|| headers['x-appengine-region']
|
|
844
|
+
|| headers['x-appengine-region'];
|
|
860
845
|
|
|
861
|
-
|
|
862
|
-
|| 'Unknown'
|
|
863
|
-
)
|
|
864
|
-
.split(',')[0]
|
|
865
|
-
.trim();
|
|
846
|
+
return value ? value.split(',')[0].trim() : null;
|
|
866
847
|
}
|
|
867
848
|
|
|
868
849
|
BackendAssistant.prototype.getHeaderCity = function (headers) {
|
|
869
850
|
headers = headers || {};
|
|
870
851
|
|
|
871
|
-
|
|
852
|
+
const value =
|
|
872
853
|
// these are present for cloudflare requests (11/21/2020)
|
|
873
854
|
headers['cf-ipcity']
|
|
874
855
|
|
|
875
|
-
|| headers['x-appengine-city']
|
|
856
|
+
|| headers['x-appengine-city'];
|
|
876
857
|
|
|
877
|
-
|
|
878
|
-
|| 'Unknown'
|
|
879
|
-
)
|
|
880
|
-
.split(',')[0]
|
|
881
|
-
.trim();
|
|
858
|
+
return value ? value.split(',')[0].trim() : null;
|
|
882
859
|
}
|
|
883
860
|
|
|
884
861
|
BackendAssistant.prototype.getHeaderLatitude = function (headers) {
|
|
@@ -917,33 +894,27 @@ BackendAssistant.prototype.getHeaderLongitude = function (headers) {
|
|
|
917
894
|
BackendAssistant.prototype.getHeaderUserAgent = function (headers) {
|
|
918
895
|
headers = headers || {};
|
|
919
896
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
)
|
|
924
|
-
.trim();
|
|
897
|
+
const value = headers['user-agent'];
|
|
898
|
+
|
|
899
|
+
return value ? value.trim() : null;
|
|
925
900
|
}
|
|
926
901
|
|
|
927
902
|
BackendAssistant.prototype.getHeaderLanguage = function (headers) {
|
|
928
903
|
headers = headers || {};
|
|
929
904
|
|
|
930
|
-
|
|
905
|
+
const value =
|
|
931
906
|
headers['accept-language']
|
|
932
|
-
|| headers['x-orig-accept-language']
|
|
933
|
-
|
|
934
|
-
)
|
|
935
|
-
.trim();
|
|
907
|
+
|| headers['x-orig-accept-language'];
|
|
908
|
+
|
|
909
|
+
return value ? value.trim() : null;
|
|
936
910
|
}
|
|
937
911
|
|
|
938
912
|
BackendAssistant.prototype.getHeaderPlatform = function (headers) {
|
|
939
913
|
headers = headers || {};
|
|
940
914
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
)
|
|
945
|
-
.replace(/"/ig, '')
|
|
946
|
-
.trim();
|
|
915
|
+
const value = headers['sec-ch-ua-platform'];
|
|
916
|
+
|
|
917
|
+
return value ? value.replace(/"/ig, '').trim() : null;
|
|
947
918
|
}
|
|
948
919
|
|
|
949
920
|
BackendAssistant.prototype.getHeaderMobile = function (headers) {
|
|
@@ -959,7 +930,7 @@ BackendAssistant.prototype.getHeaderUrl = function (headers) {
|
|
|
959
930
|
const self = this;
|
|
960
931
|
headers = headers || {};
|
|
961
932
|
|
|
962
|
-
|
|
933
|
+
const value =
|
|
963
934
|
// Origin header (most reliable for CORS requests)
|
|
964
935
|
headers['origin']
|
|
965
936
|
|
|
@@ -968,12 +939,9 @@ BackendAssistant.prototype.getHeaderUrl = function (headers) {
|
|
|
968
939
|
|| headers['referer']
|
|
969
940
|
|
|
970
941
|
// Reconstruct from host and path if available
|
|
971
|
-
|| (headers['host'] ? `https://${headers['host']}${self.ref.req?.originalUrl || self.ref.req?.url || ''}` :
|
|
942
|
+
|| (headers['host'] ? `https://${headers['host']}${self.ref.req?.originalUrl || self.ref.req?.url || ''}` : null);
|
|
972
943
|
|
|
973
|
-
|
|
974
|
-
|| ''
|
|
975
|
-
)
|
|
976
|
-
.trim();
|
|
944
|
+
return value ? value.trim() : null;
|
|
977
945
|
}
|
|
978
946
|
|
|
979
947
|
/**
|
|
@@ -78,7 +78,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
78
78
|
const strippedUrl = stripUrl(url);
|
|
79
79
|
|
|
80
80
|
// Log
|
|
81
|
-
assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city}) [${method} > ${strippedUrl}]`, safeStringify(data));
|
|
81
|
+
assistant.log(`Middleware.process(): Request (${geolocation.ip || 'unknown'} @ ${geolocation.country || '?'}, ${geolocation.region || '?'}, ${geolocation.city || '?'}) [${method} > ${strippedUrl}]`, safeStringify(data));
|
|
82
82
|
assistant.log(`Middleware.process(): Headers`, safeStringify(headers));
|
|
83
83
|
|
|
84
84
|
// Set paths
|
|
@@ -133,6 +133,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
133
133
|
const uuid = assistant?.usage?.user?.auth?.uid
|
|
134
134
|
|| assistant.request.user.auth.uid
|
|
135
135
|
|| assistant.request.geolocation.ip
|
|
136
|
+
|| 'unknown'
|
|
136
137
|
|
|
137
138
|
assistant.analytics = Manager.Analytics({
|
|
138
139
|
assistant: assistant,
|
|
@@ -59,7 +59,7 @@ Usage.prototype.init = function (assistant, options) {
|
|
|
59
59
|
self.storage = Manager.storage({name: 'usage', temporary: true, clear: options.clear, log: options.log});
|
|
60
60
|
|
|
61
61
|
// Set local key
|
|
62
|
-
self.key = (options.key || self.assistant.request.geolocation.ip || '')
|
|
62
|
+
self.key = (options.key || self.assistant.request.geolocation.ip || 'unknown')
|
|
63
63
|
// .replace(/[\.:]/g, '_');
|
|
64
64
|
|
|
65
65
|
// Set paths
|
|
@@ -51,7 +51,7 @@ function User(Manager, settings, options) {
|
|
|
51
51
|
timestampUNIX: getWithDefault(settings?.subscription?.expires?.timestampUNIX, oldDateUNIX, defaults),
|
|
52
52
|
},
|
|
53
53
|
trial: {
|
|
54
|
-
|
|
54
|
+
claimed: getWithDefault(settings?.subscription?.trial?.claimed, false, defaults),
|
|
55
55
|
expires: {
|
|
56
56
|
timestamp: getWithDefault(settings?.subscription?.trial?.expires?.timestamp, oldDate, defaults),
|
|
57
57
|
timestampUNIX: getWithDefault(settings?.subscription?.trial?.expires?.timestampUNIX, oldDateUNIX, defaults),
|
|
@@ -109,24 +109,24 @@ function User(Manager, settings, options) {
|
|
|
109
109
|
timestampUNIX: getWithDefault(settings?.activity?.created?.timestampUNIX, nowUNIX, defaults),
|
|
110
110
|
},
|
|
111
111
|
geolocation: {
|
|
112
|
-
ip:
|
|
113
|
-
continent:
|
|
114
|
-
country:
|
|
115
|
-
region:
|
|
116
|
-
city:
|
|
112
|
+
ip: settings?.activity?.geolocation?.ip ?? null,
|
|
113
|
+
continent: settings?.activity?.geolocation?.continent ?? null,
|
|
114
|
+
country: settings?.activity?.geolocation?.country ?? null,
|
|
115
|
+
region: settings?.activity?.geolocation?.region ?? null,
|
|
116
|
+
city: settings?.activity?.geolocation?.city ?? null,
|
|
117
117
|
latitude: getWithDefault(settings?.activity?.geolocation?.latitude, 0, defaults),
|
|
118
118
|
longitude: getWithDefault(settings?.activity?.geolocation?.longitude, 0, defaults),
|
|
119
119
|
},
|
|
120
120
|
client: {
|
|
121
|
-
language:
|
|
121
|
+
language: settings?.activity?.client?.language ?? null,
|
|
122
122
|
mobile: getWithDefault(settings?.activity?.client?.mobile, false, defaults),
|
|
123
|
-
device:
|
|
124
|
-
platform:
|
|
125
|
-
browser:
|
|
126
|
-
vendor:
|
|
127
|
-
runtime:
|
|
128
|
-
userAgent:
|
|
129
|
-
url:
|
|
123
|
+
device: settings?.activity?.client?.device ?? null,
|
|
124
|
+
platform: settings?.activity?.client?.platform ?? null,
|
|
125
|
+
browser: settings?.activity?.client?.browser ?? null,
|
|
126
|
+
vendor: settings?.activity?.client?.vendor ?? null,
|
|
127
|
+
runtime: settings?.activity?.client?.runtime ?? null,
|
|
128
|
+
userAgent: settings?.activity?.client?.userAgent ?? null,
|
|
129
|
+
url: settings?.activity?.client?.url ?? null,
|
|
130
130
|
},
|
|
131
131
|
},
|
|
132
132
|
api: {
|
|
@@ -138,7 +138,7 @@ function User(Manager, settings, options) {
|
|
|
138
138
|
period: getWithDefault(settings?.usage?.requests?.period, 0, defaults),
|
|
139
139
|
total: getWithDefault(settings?.usage?.requests?.total, 0, defaults),
|
|
140
140
|
last: {
|
|
141
|
-
id:
|
|
141
|
+
id: settings?.usage?.requests?.last?.id ?? null,
|
|
142
142
|
timestamp: getWithDefault(settings?.usage?.requests?.last?.timestamp, oldDate, defaults),
|
|
143
143
|
timestampUNIX: getWithDefault(settings?.usage?.requests?.last?.timestampUNIX, oldDateUNIX, defaults),
|
|
144
144
|
},
|
|
@@ -149,19 +149,19 @@ function User(Manager, settings, options) {
|
|
|
149
149
|
timestamp: getWithDefault(settings?.personal?.birthday?.timestamp, oldDate, defaults),
|
|
150
150
|
timestampUNIX: getWithDefault(settings?.personal?.birthday?.timestampUNIX, oldDateUNIX, defaults),
|
|
151
151
|
},
|
|
152
|
-
gender:
|
|
152
|
+
gender: settings?.personal?.gender ?? null,
|
|
153
153
|
location: {
|
|
154
|
-
country:
|
|
155
|
-
region:
|
|
156
|
-
city:
|
|
154
|
+
country: settings?.personal?.location?.country ?? null,
|
|
155
|
+
region: settings?.personal?.location?.region ?? null,
|
|
156
|
+
city: settings?.personal?.location?.city ?? null,
|
|
157
157
|
},
|
|
158
158
|
name: {
|
|
159
|
-
first:
|
|
160
|
-
last:
|
|
159
|
+
first: settings?.personal?.name?.first ?? null,
|
|
160
|
+
last: settings?.personal?.name?.last ?? null,
|
|
161
161
|
},
|
|
162
162
|
company: {
|
|
163
|
-
name:
|
|
164
|
-
position:
|
|
163
|
+
name: settings?.personal?.company?.name ?? null,
|
|
164
|
+
position: settings?.personal?.company?.position ?? null,
|
|
165
165
|
},
|
|
166
166
|
telephone: {
|
|
167
167
|
countryCode: getWithDefault(settings?.personal?.telephone?.countryCode, 0, defaults),
|
package/src/manager/index.js
CHANGED
|
@@ -1111,37 +1111,6 @@ Manager.prototype.setupCustomServer = function (_library, options) {
|
|
|
1111
1111
|
});
|
|
1112
1112
|
}
|
|
1113
1113
|
|
|
1114
|
-
// Setup Custom Server
|
|
1115
|
-
Manager.prototype.getApp = function (id) {
|
|
1116
|
-
const self = this;
|
|
1117
|
-
|
|
1118
|
-
// Get the app
|
|
1119
|
-
return new Promise(function(resolve, reject) {
|
|
1120
|
-
const fetch = require('wonderful-fetch');
|
|
1121
|
-
|
|
1122
|
-
// Set ID
|
|
1123
|
-
id = id || self.config.app.id;
|
|
1124
|
-
|
|
1125
|
-
// If no ID, reject
|
|
1126
|
-
if (!id) {
|
|
1127
|
-
return reject(new Error('No ID provided'));
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// Fetch the app
|
|
1131
|
-
fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/getApp`, {
|
|
1132
|
-
method: 'post',
|
|
1133
|
-
response: 'json',
|
|
1134
|
-
timeout: 30000,
|
|
1135
|
-
tries: 3,
|
|
1136
|
-
body: {
|
|
1137
|
-
id: id,
|
|
1138
|
-
}
|
|
1139
|
-
})
|
|
1140
|
-
.then((r) => resolve(r))
|
|
1141
|
-
.catch((e) => reject(e));
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
1114
|
function resolveProjectPackage(dir) {
|
|
1146
1115
|
try {
|
|
1147
1116
|
return require(path.resolve(dir, 'functions', 'package.json'));
|
|
@@ -243,7 +243,7 @@ OpenAI.prototype.request = function (options) {
|
|
|
243
243
|
// Load prompt
|
|
244
244
|
const prompt = loadContent(options.prompt, _log);
|
|
245
245
|
const message = loadContent(options.message, _log);
|
|
246
|
-
const user = options.user?.auth?.uid || assistant.request.geolocation.ip;
|
|
246
|
+
const user = options.user?.auth?.uid || assistant.request.geolocation.ip || 'unknown';
|
|
247
247
|
|
|
248
248
|
// Log
|
|
249
249
|
_log('Prompt', prompt);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /app - Public app configuration
|
|
3
|
+
* Returns a safe subset of the project's config (no secrets)
|
|
4
|
+
*/
|
|
5
|
+
module.exports = async ({ assistant, Manager }) => {
|
|
6
|
+
const config = Manager.config;
|
|
7
|
+
|
|
8
|
+
return assistant.respond(buildPublicConfig(config));
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build a public-safe config object from Manager.config
|
|
13
|
+
* Excludes sensitive fields: sentry, google_analytics, ghostii, etc.
|
|
14
|
+
*/
|
|
15
|
+
function buildPublicConfig(config) {
|
|
16
|
+
return {
|
|
17
|
+
id: config.app?.id,
|
|
18
|
+
name: config.brand?.name,
|
|
19
|
+
description: config.brand?.description,
|
|
20
|
+
url: config.brand?.url,
|
|
21
|
+
email: config.brand?.contact?.email,
|
|
22
|
+
images: config.brand?.images || {},
|
|
23
|
+
github: {
|
|
24
|
+
user: config.github?.user,
|
|
25
|
+
repo: (config.github?.repo_website || '').split('/').pop(),
|
|
26
|
+
},
|
|
27
|
+
authentication: config.authentication || {},
|
|
28
|
+
reviews: config.reviews || {},
|
|
29
|
+
firebaseConfig: config.firebaseConfig || {},
|
|
30
|
+
products: (config.products || []).map(p => ({
|
|
31
|
+
id: p.id,
|
|
32
|
+
name: p.name,
|
|
33
|
+
type: p.type,
|
|
34
|
+
limits: p.limits || {},
|
|
35
|
+
trial: p.trial || {},
|
|
36
|
+
prices: p.prices || {},
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports.buildPublicConfig = buildPublicConfig;
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
const { merge } = require('lodash');
|
|
6
6
|
|
|
7
7
|
module.exports = async ({ assistant, Manager, analytics }) => {
|
|
8
|
-
const fetch = Manager.require('wonderful-fetch');
|
|
9
|
-
|
|
10
8
|
const defaultProviders = {
|
|
11
9
|
['password']: {
|
|
12
10
|
enabled: true,
|
|
@@ -34,14 +32,8 @@ module.exports = async ({ assistant, Manager, analytics }) => {
|
|
|
34
32
|
},
|
|
35
33
|
};
|
|
36
34
|
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
if (appObject instanceof Error) {
|
|
40
|
-
return assistant.respond(`Failed to get app object: ${appObject}`, { code: 500 });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Merge the default providers with the app providers
|
|
44
|
-
const providers = merge(defaultProviders, appObject.authentication);
|
|
35
|
+
// Merge the default providers with the config providers
|
|
36
|
+
const providers = merge(defaultProviders, Manager.config.authentication || {});
|
|
45
37
|
|
|
46
38
|
// Reformat the object so it's just provider=true/false
|
|
47
39
|
const finalProviders = {};
|
|
@@ -56,25 +48,3 @@ module.exports = async ({ assistant, Manager, analytics }) => {
|
|
|
56
48
|
|
|
57
49
|
return assistant.respond(finalProviders);
|
|
58
50
|
};
|
|
59
|
-
|
|
60
|
-
// Helper: Get app object from ITW
|
|
61
|
-
async function getAppObject(Manager, assistant) {
|
|
62
|
-
const fetch = Manager.require('wonderful-fetch');
|
|
63
|
-
const id = Manager.config.app.id;
|
|
64
|
-
|
|
65
|
-
const result = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/getApp', {
|
|
66
|
-
method: 'post',
|
|
67
|
-
response: 'json',
|
|
68
|
-
body: {
|
|
69
|
-
id: id,
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
assistant.log('getAppObject(): Response', result);
|
|
74
|
-
|
|
75
|
-
if (!result) {
|
|
76
|
-
throw new Error(`App with id ${id} not found`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return result;
|
|
80
|
-
}
|
|
@@ -43,7 +43,7 @@ module.exports = async ({ assistant, Manager, settings, analytics }) => {
|
|
|
43
43
|
|
|
44
44
|
// Check spam filter using local storage
|
|
45
45
|
const storage = Manager.storage({ temporary: true });
|
|
46
|
-
const ipPath = ['api:general:email', 'ips', assistant.request.geolocation.ip];
|
|
46
|
+
const ipPath = ['api:general:email', 'ips', assistant.request.geolocation.ip || 'unknown'];
|
|
47
47
|
const emailPath = ['api:general:email', 'emails', settings.email];
|
|
48
48
|
|
|
49
49
|
const ipData = storage.get(ipPath).value() || {};
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* POST /special/electron-client - Setup Electron Manager client
|
|
3
3
|
* Returns client configuration with optional auth
|
|
4
4
|
*/
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { buildPublicConfig } = require(path.join(__dirname, '..', '..', 'app', 'get.js'));
|
|
7
|
+
|
|
5
8
|
module.exports = async ({ assistant, Manager, settings, analytics, libraries }) => {
|
|
6
|
-
const fetch = Manager.require('wonderful-fetch');
|
|
7
9
|
const { admin } = libraries;
|
|
8
10
|
|
|
9
11
|
// appId/app fallback to Manager.config
|
|
10
12
|
let uid = settings.uid;
|
|
11
|
-
const app = settings.appId || settings.app || Manager.config.app.id;
|
|
12
13
|
let config = settings.config;
|
|
13
14
|
|
|
14
15
|
let uuid = null;
|
|
@@ -42,21 +43,6 @@ module.exports = async ({ assistant, Manager, settings, analytics, libraries })
|
|
|
42
43
|
config = {};
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
// Fetch app details
|
|
46
|
-
const appDetails = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/getApp', {
|
|
47
|
-
method: 'post',
|
|
48
|
-
timeout: 30000,
|
|
49
|
-
tries: 3,
|
|
50
|
-
response: 'json',
|
|
51
|
-
body: {
|
|
52
|
-
id: app,
|
|
53
|
-
},
|
|
54
|
-
}).catch(e => e);
|
|
55
|
-
|
|
56
|
-
if (appDetails instanceof Error) {
|
|
57
|
-
return assistant.respond(`Error fetching app details: ${appDetails}`, { code: 500 });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
46
|
// Track analytics
|
|
61
47
|
analytics.event('special/electron-client', { action: 'setup' });
|
|
62
48
|
|
|
@@ -66,7 +52,7 @@ module.exports = async ({ assistant, Manager, settings, analytics, libraries })
|
|
|
66
52
|
timestamp: new Date().toISOString(),
|
|
67
53
|
ip: assistant.request.geolocation.ip,
|
|
68
54
|
country: assistant.request.geolocation.country,
|
|
69
|
-
app:
|
|
55
|
+
app: buildPublicConfig(Manager.config),
|
|
70
56
|
config: config,
|
|
71
57
|
});
|
|
72
58
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const pushid = require('pushid');
|
|
2
|
-
const fetch = require('wonderful-fetch');
|
|
3
2
|
const powertools = require('node-powertools');
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -30,17 +29,8 @@ module.exports = async ({ assistant, Manager, user, settings, libraries }) => {
|
|
|
30
29
|
decision.promptReview = true;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
// Get
|
|
34
|
-
const
|
|
35
|
-
method: 'post',
|
|
36
|
-
response: 'json',
|
|
37
|
-
body: { id: Manager.config.app.id },
|
|
38
|
-
}).catch((e) => {
|
|
39
|
-
assistant.error(`Failed to get app: ${e.message}`);
|
|
40
|
-
return {};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const reviews = appResponse.reviews || {};
|
|
32
|
+
// Get review config from local config
|
|
33
|
+
const reviews = { ...(Manager.config.reviews || {}) };
|
|
44
34
|
reviews.enabled = typeof reviews.enabled === 'undefined' ? true : reviews.enabled;
|
|
45
35
|
reviews.sites = reviews.sites || [];
|
|
46
36
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = () => ({});
|
|
@@ -2,7 +2,7 @@ const uuid = require('uuid');
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Helper to create a future expiration date for premium subscriptions
|
|
5
|
-
*
|
|
5
|
+
* User() checks subscription.expires to determine if subscription is active
|
|
6
6
|
* If expires is in the past (or default 1970), subscription gets downgraded to basic
|
|
7
7
|
*/
|
|
8
8
|
function getFutureExpires(years = 10) {
|
|
@@ -38,7 +38,7 @@ function getPastExpires(years = 1) {
|
|
|
38
38
|
* - properties: Object to merge into user doc after auth:on-create
|
|
39
39
|
*
|
|
40
40
|
* IMPORTANT: Premium accounts MUST have subscription.expires set to a future date
|
|
41
|
-
*
|
|
41
|
+
* and subscription.status set to 'active'
|
|
42
42
|
*/
|
|
43
43
|
const STATIC_ACCOUNTS = {
|
|
44
44
|
admin: {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
brand: {
|
|
3
3
|
id: 'my-app',
|
|
4
4
|
name: 'My Brand',
|
|
5
|
+
description: '',
|
|
5
6
|
url: 'https://example.com',
|
|
6
7
|
contact: {
|
|
7
8
|
email: 'support@example.com',
|
|
@@ -16,6 +17,14 @@
|
|
|
16
17
|
user: 'username',
|
|
17
18
|
repo_website: 'https://github.com/username/backend-manager',
|
|
18
19
|
},
|
|
20
|
+
authentication: {
|
|
21
|
+
'password': { enabled: true },
|
|
22
|
+
'google.com': { enabled: true },
|
|
23
|
+
},
|
|
24
|
+
reviews: {
|
|
25
|
+
enabled: true,
|
|
26
|
+
sites: [],
|
|
27
|
+
},
|
|
19
28
|
sentry: {
|
|
20
29
|
dsn: 'https://d965557418748jd749d837asf00552f@o777489.ingest.sentry.io/8789941',
|
|
21
30
|
},
|
|
@@ -45,6 +54,8 @@
|
|
|
45
54
|
prompt: '',
|
|
46
55
|
chance: 1.0,
|
|
47
56
|
author: 'alex-raeburn',
|
|
57
|
+
// app: 'other-app-id', // Optional: target a different app
|
|
58
|
+
// appUrl: 'https://api.otherapp.com', // Required if app is set (fetches /backend-manager/app)
|
|
48
59
|
}
|
|
49
60
|
],
|
|
50
61
|
products: [
|
package/test/routes/user/user.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Test: GET /user
|
|
3
3
|
* Tests the user resolve endpoint
|
|
4
|
-
* Returns
|
|
5
|
-
* Validates that
|
|
4
|
+
* Returns user account info for authenticated users
|
|
5
|
+
* Validates that User() correctly structures user data
|
|
6
6
|
*/
|
|
7
7
|
module.exports = {
|
|
8
8
|
description: 'User resolve (account info)',
|
|
@@ -129,9 +129,9 @@ module.exports = {
|
|
|
129
129
|
},
|
|
130
130
|
},
|
|
131
131
|
|
|
132
|
-
// Test 6: Premium expired user - verify subscription
|
|
132
|
+
// Test 6: Premium expired user - verify subscription retains product but shows cancelled status
|
|
133
133
|
{
|
|
134
|
-
name: 'premium-expired-user-
|
|
134
|
+
name: 'premium-expired-user-cancelled',
|
|
135
135
|
auth: 'premium-expired',
|
|
136
136
|
timeout: 15000,
|
|
137
137
|
|
|
@@ -145,8 +145,9 @@ module.exports = {
|
|
|
145
145
|
// Verify auth properties
|
|
146
146
|
assert.equal(user.auth.uid, accounts['premium-expired'].uid, 'UID should match expired premium test account');
|
|
147
147
|
|
|
148
|
-
// Verify subscription -
|
|
149
|
-
assert.equal(user.subscription.product.id, '
|
|
148
|
+
// Verify subscription - product.id stays premium, status reflects cancellation
|
|
149
|
+
assert.equal(user.subscription.product.id, 'premium', 'Product ID should remain premium');
|
|
150
|
+
assert.equal(user.subscription.status, 'cancelled', 'Status should be cancelled');
|
|
150
151
|
},
|
|
151
152
|
},
|
|
152
153
|
],
|