diff options
author | Stefan Malzner <stefan@adlk.io> | 2017-10-13 12:29:40 +0200 |
---|---|---|
committer | Stefan Malzner <stefan@adlk.io> | 2017-10-13 12:29:40 +0200 |
commit | 58cda9cc7fb79ca9df6746de7f9662bc08dc156a (patch) | |
tree | 1211600c2a5d3b5f81c435c6896618111a611720 /src/api | |
download | ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.gz ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.zst ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.zip |
initial commit
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/AppApi.js | 9 | ||||
-rw-r--r-- | src/api/LocalApi.js | 18 | ||||
-rw-r--r-- | src/api/NewsApi.js | 14 | ||||
-rw-r--r-- | src/api/PaymentApi.js | 22 | ||||
-rw-r--r-- | src/api/RecipePreviewsApi.js | 17 | ||||
-rw-r--r-- | src/api/RecipesApi.js | 17 | ||||
-rw-r--r-- | src/api/ServicesApi.js | 33 | ||||
-rw-r--r-- | src/api/UserApi.js | 49 | ||||
-rw-r--r-- | src/api/index.js | 19 | ||||
-rw-r--r-- | src/api/server/LocalApi.js | 33 | ||||
-rw-r--r-- | src/api/server/ServerApi.js | 574 |
11 files changed, 805 insertions, 0 deletions
diff --git a/src/api/AppApi.js b/src/api/AppApi.js new file mode 100644 index 000000000..411c187f4 --- /dev/null +++ b/src/api/AppApi.js | |||
@@ -0,0 +1,9 @@ | |||
1 | export default class AppApi { | ||
2 | constructor(server) { | ||
3 | this.server = server; | ||
4 | } | ||
5 | |||
6 | health() { | ||
7 | return this.server.healthCheck(); | ||
8 | } | ||
9 | } | ||
diff --git a/src/api/LocalApi.js b/src/api/LocalApi.js new file mode 100644 index 000000000..6f2b049d6 --- /dev/null +++ b/src/api/LocalApi.js | |||
@@ -0,0 +1,18 @@ | |||
1 | export default class LocalApi { | ||
2 | constructor(server, local) { | ||
3 | this.server = server; | ||
4 | this.local = local; | ||
5 | } | ||
6 | |||
7 | getSettings() { | ||
8 | return this.local.getAppSettings(); | ||
9 | } | ||
10 | |||
11 | updateSettings(data) { | ||
12 | return this.local.updateAppSettings(data); | ||
13 | } | ||
14 | |||
15 | removeKey(key) { | ||
16 | return this.local.removeKey(key); | ||
17 | } | ||
18 | } | ||
diff --git a/src/api/NewsApi.js b/src/api/NewsApi.js new file mode 100644 index 000000000..294957511 --- /dev/null +++ b/src/api/NewsApi.js | |||
@@ -0,0 +1,14 @@ | |||
1 | export default class NewsApi { | ||
2 | constructor(server, local) { | ||
3 | this.server = server; | ||
4 | this.local = local; | ||
5 | } | ||
6 | |||
7 | latest() { | ||
8 | return this.server.getLatestNews(); | ||
9 | } | ||
10 | |||
11 | hide(id) { | ||
12 | return this.server.hideNews(id); | ||
13 | } | ||
14 | } | ||
diff --git a/src/api/PaymentApi.js b/src/api/PaymentApi.js new file mode 100644 index 000000000..3f6bb442e --- /dev/null +++ b/src/api/PaymentApi.js | |||
@@ -0,0 +1,22 @@ | |||
1 | export default class PaymentApi { | ||
2 | constructor(server, local) { | ||
3 | this.server = server; | ||
4 | this.local = local; | ||
5 | } | ||
6 | |||
7 | plans() { | ||
8 | return this.server.getPlans(); | ||
9 | } | ||
10 | |||
11 | getHostedPage(planId) { | ||
12 | return this.server.getHostedPage(planId); | ||
13 | } | ||
14 | |||
15 | getDashboardUrl() { | ||
16 | return this.server.getPaymentDashboardUrl(); | ||
17 | } | ||
18 | |||
19 | getOrders() { | ||
20 | return this.server.getSubscriptionOrders(); | ||
21 | } | ||
22 | } | ||
diff --git a/src/api/RecipePreviewsApi.js b/src/api/RecipePreviewsApi.js new file mode 100644 index 000000000..d9c675d76 --- /dev/null +++ b/src/api/RecipePreviewsApi.js | |||
@@ -0,0 +1,17 @@ | |||
1 | export default class ServicesApi { | ||
2 | constructor(server) { | ||
3 | this.server = server; | ||
4 | } | ||
5 | |||
6 | all() { | ||
7 | return this.server.getRecipePreviews(); | ||
8 | } | ||
9 | |||
10 | featured() { | ||
11 | return this.server.getFeaturedRecipePreviews(); | ||
12 | } | ||
13 | |||
14 | search(needle) { | ||
15 | return this.server.searchRecipePreviews(needle); | ||
16 | } | ||
17 | } | ||
diff --git a/src/api/RecipesApi.js b/src/api/RecipesApi.js new file mode 100644 index 000000000..0573dacaf --- /dev/null +++ b/src/api/RecipesApi.js | |||
@@ -0,0 +1,17 @@ | |||
1 | export default class ServicesApi { | ||
2 | constructor(server) { | ||
3 | this.server = server; | ||
4 | } | ||
5 | |||
6 | all() { | ||
7 | return this.server.getInstalledRecipes(); | ||
8 | } | ||
9 | |||
10 | install(recipeId) { | ||
11 | return this.server.getRecipePackage(recipeId); | ||
12 | } | ||
13 | |||
14 | update(recipes) { | ||
15 | return this.server.getRecipeUpdates(recipes); | ||
16 | } | ||
17 | } | ||
diff --git a/src/api/ServicesApi.js b/src/api/ServicesApi.js new file mode 100644 index 000000000..3cb40ba0d --- /dev/null +++ b/src/api/ServicesApi.js | |||
@@ -0,0 +1,33 @@ | |||
1 | export default class ServicesApi { | ||
2 | constructor(server) { | ||
3 | this.server = server; | ||
4 | } | ||
5 | |||
6 | all() { | ||
7 | return this.server.getServices(); | ||
8 | } | ||
9 | |||
10 | // one(customerId) { | ||
11 | // return this.server.getCustomer(customerId); | ||
12 | // } | ||
13 | // | ||
14 | // search(needle) { | ||
15 | // return this.server.searchCustomers(needle); | ||
16 | // } | ||
17 | // | ||
18 | create(recipeId, data) { | ||
19 | return this.server.createService(recipeId, data); | ||
20 | } | ||
21 | |||
22 | delete(serviceId) { | ||
23 | return this.server.deleteService(serviceId); | ||
24 | } | ||
25 | |||
26 | update(serviceId, data) { | ||
27 | return this.server.updateService(serviceId, data); | ||
28 | } | ||
29 | |||
30 | reorder(data) { | ||
31 | return this.server.reorderService(data); | ||
32 | } | ||
33 | } | ||
diff --git a/src/api/UserApi.js b/src/api/UserApi.js new file mode 100644 index 000000000..e8fd75bed --- /dev/null +++ b/src/api/UserApi.js | |||
@@ -0,0 +1,49 @@ | |||
1 | import { hash } from '../helpers/password-helpers'; | ||
2 | |||
3 | export default class UserApi { | ||
4 | constructor(server, local) { | ||
5 | this.server = server; | ||
6 | this.local = local; | ||
7 | } | ||
8 | |||
9 | login(email, password) { | ||
10 | return this.server.login(email, hash(password)); | ||
11 | } | ||
12 | |||
13 | logout() { | ||
14 | return this; | ||
15 | } | ||
16 | |||
17 | signup(data) { | ||
18 | Object.assign(data, { | ||
19 | password: hash(data.password), | ||
20 | }); | ||
21 | return this.server.signup(data); | ||
22 | } | ||
23 | |||
24 | password(email) { | ||
25 | return this.server.retrievePassword(email); | ||
26 | } | ||
27 | |||
28 | invite(data) { | ||
29 | return this.server.inviteUser(data); | ||
30 | } | ||
31 | |||
32 | getInfo() { | ||
33 | return this.server.userInfo(); | ||
34 | } | ||
35 | |||
36 | updateInfo(data) { | ||
37 | const userData = data; | ||
38 | if (userData.oldPassword && userData.newPassword) { | ||
39 | userData.oldPassword = hash(userData.oldPassword); | ||
40 | userData.newPassword = hash(userData.newPassword); | ||
41 | } | ||
42 | |||
43 | return this.server.updateUserInfo(userData); | ||
44 | } | ||
45 | |||
46 | getLegacyServices() { | ||
47 | return this.server.getLegacyServices(); | ||
48 | } | ||
49 | } | ||
diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 000000000..3fc18c4b5 --- /dev/null +++ b/src/api/index.js | |||
@@ -0,0 +1,19 @@ | |||
1 | import AppApi from './AppApi'; | ||
2 | import ServicesApi from './ServicesApi'; | ||
3 | import RecipePreviewsApi from './RecipePreviewsApi'; | ||
4 | import RecipesApi from './RecipesApi'; | ||
5 | import UserApi from './UserApi'; | ||
6 | import LocalApi from './LocalApi'; | ||
7 | import PaymentApi from './PaymentApi'; | ||
8 | import NewsApi from './NewsApi'; | ||
9 | |||
10 | export default (server, local) => ({ | ||
11 | app: new AppApi(server, local), | ||
12 | services: new ServicesApi(server, local), | ||
13 | recipePreviews: new RecipePreviewsApi(server, local), | ||
14 | recipes: new RecipesApi(server, local), | ||
15 | user: new UserApi(server, local), | ||
16 | local: new LocalApi(server, local), | ||
17 | payment: new PaymentApi(server, local), | ||
18 | news: new NewsApi(server, local), | ||
19 | }); | ||
diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js new file mode 100644 index 000000000..79ac6e12f --- /dev/null +++ b/src/api/server/LocalApi.js | |||
@@ -0,0 +1,33 @@ | |||
1 | export default class LocalApi { | ||
2 | // App | ||
3 | async updateAppSettings(data) { | ||
4 | const currentSettings = await this.getAppSettings(); | ||
5 | const settings = Object.assign(currentSettings, data); | ||
6 | |||
7 | localStorage.setItem('app', JSON.stringify(settings)); | ||
8 | console.debug('LocalApi::updateAppSettings resolves', settings); | ||
9 | |||
10 | return settings; | ||
11 | } | ||
12 | |||
13 | async getAppSettings() { | ||
14 | const settingsString = localStorage.getItem('app'); | ||
15 | try { | ||
16 | const settings = JSON.parse(settingsString) || {}; | ||
17 | console.debug('LocalApi::getAppSettings resolves', settings); | ||
18 | |||
19 | return settings; | ||
20 | } catch (err) { | ||
21 | return {}; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | async removeKey(key) { | ||
26 | const settings = await this.getAppSettings(); | ||
27 | |||
28 | if (Object.hasOwnProperty.call(settings, key)) { | ||
29 | delete settings[key]; | ||
30 | localStorage.setItem('app', JSON.stringify(settings)); | ||
31 | } | ||
32 | } | ||
33 | } | ||
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js new file mode 100644 index 000000000..b369796e8 --- /dev/null +++ b/src/api/server/ServerApi.js | |||
@@ -0,0 +1,574 @@ | |||
1 | import os from 'os'; | ||
2 | import path from 'path'; | ||
3 | import targz from 'tar.gz'; | ||
4 | import fs from 'fs-extra'; | ||
5 | import { remote } from 'electron'; | ||
6 | |||
7 | import ServiceModel from '../../models/Service'; | ||
8 | import RecipePreviewModel from '../../models/RecipePreview'; | ||
9 | import RecipeModel from '../../models/Recipe'; | ||
10 | import PlanModel from '../../models/Plan'; | ||
11 | import NewsModel from '../../models/News'; | ||
12 | import UserModel from '../../models/User'; | ||
13 | import OrderModel from '../../models/Order'; | ||
14 | |||
15 | import { API } from '../../environment'; | ||
16 | |||
17 | import { | ||
18 | getRecipeDirectory, | ||
19 | getDevRecipeDirectory, | ||
20 | loadRecipeConfig, | ||
21 | } from '../../helpers/recipe-helpers'; | ||
22 | |||
23 | module.paths.unshift( | ||
24 | getDevRecipeDirectory(), | ||
25 | getRecipeDirectory(), | ||
26 | ); | ||
27 | |||
28 | const { app } = remote; | ||
29 | const fetch = remote.require('electron-fetch'); | ||
30 | |||
31 | const SERVER_URL = API; | ||
32 | const API_VERSION = 'v1'; | ||
33 | |||
34 | export default class ServerApi { | ||
35 | recipePreviews = []; | ||
36 | recipes = []; | ||
37 | |||
38 | // User | ||
39 | async login(email, passwordHash) { | ||
40 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/auth/login`, this._prepareAuthRequest({ | ||
41 | method: 'POST', | ||
42 | headers: { | ||
43 | Authorization: `Basic ${window.btoa(`${email}:${passwordHash}`)}`, | ||
44 | }, | ||
45 | }, false)); | ||
46 | if (!request.ok) { | ||
47 | throw request; | ||
48 | } | ||
49 | const u = await request.json(); | ||
50 | |||
51 | console.debug('ServerApi::login resolves', u); | ||
52 | return u.token; | ||
53 | } | ||
54 | |||
55 | async signup(data) { | ||
56 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/auth/signup`, this._prepareAuthRequest({ | ||
57 | method: 'POST', | ||
58 | body: JSON.stringify(data), | ||
59 | }, false)); | ||
60 | if (!request.ok) { | ||
61 | throw request; | ||
62 | } | ||
63 | const u = await request.json(); | ||
64 | |||
65 | console.debug('ServerApi::signup resolves', u); | ||
66 | return u.token; | ||
67 | } | ||
68 | |||
69 | async inviteUser(data) { | ||
70 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/invite`, this._prepareAuthRequest({ | ||
71 | method: 'POST', | ||
72 | body: JSON.stringify(data), | ||
73 | })); | ||
74 | if (!request.ok) { | ||
75 | throw request; | ||
76 | } | ||
77 | |||
78 | console.debug('ServerApi::inviteUser'); | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | async retrievePassword(email) { | ||
83 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/auth/password`, this._prepareAuthRequest({ | ||
84 | method: 'POST', | ||
85 | body: JSON.stringify({ | ||
86 | email, | ||
87 | }), | ||
88 | }, false)); | ||
89 | if (!request.ok) { | ||
90 | throw request; | ||
91 | } | ||
92 | const r = await request.json(); | ||
93 | |||
94 | console.debug('ServerApi::retrievePassword'); | ||
95 | return r; | ||
96 | } | ||
97 | |||
98 | async userInfo() { | ||
99 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me`, this._prepareAuthRequest({ | ||
100 | method: 'GET', | ||
101 | })); | ||
102 | if (!request.ok) { | ||
103 | throw request; | ||
104 | } | ||
105 | const data = await request.json(); | ||
106 | |||
107 | const user = new UserModel(data); | ||
108 | console.debug('ServerApi::userInfo resolves', user); | ||
109 | |||
110 | return user; | ||
111 | } | ||
112 | |||
113 | async updateUserInfo(data) { | ||
114 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me`, this._prepareAuthRequest({ | ||
115 | method: 'PUT', | ||
116 | body: JSON.stringify(data), | ||
117 | })); | ||
118 | if (!request.ok) { | ||
119 | throw request; | ||
120 | } | ||
121 | const updatedData = await request.json(); | ||
122 | |||
123 | const user = Object.assign(updatedData, { data: new UserModel(updatedData.data) }); | ||
124 | console.debug('ServerApi::updateUserInfo resolves', user); | ||
125 | return user; | ||
126 | } | ||
127 | |||
128 | // Services | ||
129 | async getServices() { | ||
130 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me/services`, this._prepareAuthRequest({ | ||
131 | method: 'GET', | ||
132 | })); | ||
133 | if (!request.ok) { | ||
134 | throw request; | ||
135 | } | ||
136 | const data = await request.json(); | ||
137 | |||
138 | let services = await this._mapServiceModels(data); | ||
139 | services = services.filter(service => service !== null); | ||
140 | console.debug('ServerApi::getServices resolves', services); | ||
141 | return services; | ||
142 | } | ||
143 | |||
144 | async createService(recipeId, data) { | ||
145 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service`, this._prepareAuthRequest({ | ||
146 | method: 'POST', | ||
147 | body: JSON.stringify(Object.assign({ | ||
148 | recipeId, | ||
149 | }, data)), | ||
150 | })); | ||
151 | if (!request.ok) { | ||
152 | throw request; | ||
153 | } | ||
154 | const serviceData = await request.json(); | ||
155 | const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); | ||
156 | |||
157 | console.debug('ServerApi::createService resolves', service); | ||
158 | return service; | ||
159 | } | ||
160 | |||
161 | async updateService(recipeId, data) { | ||
162 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/${recipeId}`, this._prepareAuthRequest({ | ||
163 | method: 'PUT', | ||
164 | body: JSON.stringify(data), | ||
165 | })); | ||
166 | if (!request.ok) { | ||
167 | throw request; | ||
168 | } | ||
169 | const serviceData = await request.json(); | ||
170 | const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); | ||
171 | |||
172 | console.debug('ServerApi::updateService resolves', service); | ||
173 | return service; | ||
174 | } | ||
175 | |||
176 | async reorderService(data) { | ||
177 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/reorder`, this._prepareAuthRequest({ | ||
178 | method: 'PUT', | ||
179 | body: JSON.stringify(data), | ||
180 | })); | ||
181 | if (!request.ok) { | ||
182 | throw request; | ||
183 | } | ||
184 | const serviceData = await request.json(); | ||
185 | console.debug('ServerApi::reorderService resolves', serviceData); | ||
186 | return serviceData; | ||
187 | } | ||
188 | |||
189 | async deleteService(id) { | ||
190 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/${id}`, this._prepareAuthRequest({ | ||
191 | method: 'DELETE', | ||
192 | })); | ||
193 | if (!request.ok) { | ||
194 | throw request; | ||
195 | } | ||
196 | const data = await request.json(); | ||
197 | |||
198 | console.debug('ServerApi::deleteService resolves', data); | ||
199 | return data; | ||
200 | } | ||
201 | |||
202 | // Recipes | ||
203 | async getInstalledRecipes() { | ||
204 | const recipesDirectory = getRecipeDirectory(); | ||
205 | const paths = fs.readdirSync(recipesDirectory) | ||
206 | .filter(file => ( | ||
207 | fs.statSync(path.join(recipesDirectory, file)).isDirectory() | ||
208 | && file !== 'temp' | ||
209 | && file !== 'dev' | ||
210 | )); | ||
211 | |||
212 | this.recipes = paths.map((id) => { | ||
213 | // eslint-disable-next-line | ||
214 | const Recipe = require(id)(RecipeModel); | ||
215 | return new Recipe(loadRecipeConfig(id)); | ||
216 | }).filter(recipe => recipe.id); | ||
217 | |||
218 | this.recipes = this.recipes.concat(this._getDevRecipes()); | ||
219 | |||
220 | console.debug('StubServerApi::getInstalledRecipes resolves', this.recipes); | ||
221 | return this.recipes; | ||
222 | } | ||
223 | |||
224 | async getRecipeUpdates(recipeVersions) { | ||
225 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/recipes/update`, this._prepareAuthRequest({ | ||
226 | method: 'POST', | ||
227 | body: JSON.stringify(recipeVersions), | ||
228 | })); | ||
229 | if (!request.ok) { | ||
230 | throw request; | ||
231 | } | ||
232 | const recipes = await request.json(); | ||
233 | console.debug('ServerApi::getRecipeUpdates resolves', recipes); | ||
234 | return recipes; | ||
235 | } | ||
236 | |||
237 | // Recipes Previews | ||
238 | async getRecipePreviews() { | ||
239 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/recipes`, this._prepareAuthRequest({ | ||
240 | method: 'GET', | ||
241 | })); | ||
242 | if (!request.ok) { | ||
243 | throw request; | ||
244 | } | ||
245 | const data = await request.json(); | ||
246 | |||
247 | const recipePreviews = this._mapRecipePreviewModel(data); | ||
248 | console.debug('ServerApi::getRecipes resolves', recipePreviews); | ||
249 | |||
250 | return recipePreviews; | ||
251 | } | ||
252 | |||
253 | async getFeaturedRecipePreviews() { | ||
254 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/recipes/popular`, this._prepareAuthRequest({ | ||
255 | method: 'GET', | ||
256 | })); | ||
257 | if (!request.ok) { | ||
258 | throw request; | ||
259 | } | ||
260 | const data = await request.json(); | ||
261 | |||
262 | // data = this._addLocalRecipesToPreviews(data); | ||
263 | |||
264 | const recipePreviews = this._mapRecipePreviewModel(data); | ||
265 | console.debug('ServerApi::getFeaturedRecipes resolves', recipePreviews); | ||
266 | return recipePreviews; | ||
267 | } | ||
268 | |||
269 | async searchRecipePreviews(needle) { | ||
270 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/recipes/search?needle=${needle}`, this._prepareAuthRequest({ | ||
271 | method: 'GET', | ||
272 | })); | ||
273 | if (!request.ok) { | ||
274 | throw request; | ||
275 | } | ||
276 | const data = await request.json(); | ||
277 | |||
278 | const recipePreviews = this._mapRecipePreviewModel(data); | ||
279 | console.debug('ServerApi::searchRecipePreviews resolves', recipePreviews); | ||
280 | return recipePreviews; | ||
281 | } | ||
282 | |||
283 | async getRecipePackage(recipeId) { | ||
284 | try { | ||
285 | const recipesDirectory = path.join(app.getPath('userData'), 'recipes'); | ||
286 | |||
287 | const recipeTempDirectory = path.join(recipesDirectory, 'temp', recipeId); | ||
288 | const archivePath = path.join(recipeTempDirectory, 'recipe.tar.gz'); | ||
289 | const packageUrl = `${SERVER_URL}/${API_VERSION}/recipes/download/${recipeId}`; | ||
290 | |||
291 | fs.ensureDirSync(recipeTempDirectory); | ||
292 | const res = await fetch(packageUrl); | ||
293 | const buffer = await res.buffer(); | ||
294 | fs.writeFileSync(archivePath, buffer); | ||
295 | |||
296 | await targz().extract(archivePath, recipeTempDirectory); | ||
297 | |||
298 | const { id } = fs.readJsonSync(path.join(recipeTempDirectory, 'package.json')); | ||
299 | const recipeDirectory = path.join(recipesDirectory, id); | ||
300 | |||
301 | fs.copySync(recipeTempDirectory, recipeDirectory); | ||
302 | fs.remove(recipeTempDirectory); | ||
303 | fs.remove(path.join(recipesDirectory, recipeId, 'recipe.tar.gz')); | ||
304 | |||
305 | return id; | ||
306 | } catch (err) { | ||
307 | console.error(err); | ||
308 | |||
309 | return false; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | // Payment | ||
314 | async getPlans() { | ||
315 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/payment/plans`, this._prepareAuthRequest({ | ||
316 | method: 'GET', | ||
317 | })); | ||
318 | if (!request.ok) { | ||
319 | throw request; | ||
320 | } | ||
321 | const data = await request.json(); | ||
322 | |||
323 | const plan = new PlanModel(data); | ||
324 | console.debug('ServerApi::getPlans resolves', plan); | ||
325 | return plan; | ||
326 | } | ||
327 | |||
328 | async getHostedPage(planId) { | ||
329 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/payment/init`, this._prepareAuthRequest({ | ||
330 | method: 'POST', | ||
331 | body: JSON.stringify({ | ||
332 | planId, | ||
333 | }), | ||
334 | })); | ||
335 | if (!request.ok) { | ||
336 | throw request; | ||
337 | } | ||
338 | const data = await request.json(); | ||
339 | |||
340 | console.debug('ServerApi::getHostedPage resolves', data); | ||
341 | return data; | ||
342 | } | ||
343 | |||
344 | async getPaymentDashboardUrl() { | ||
345 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me/billing`, this._prepareAuthRequest({ | ||
346 | method: 'GET', | ||
347 | })); | ||
348 | if (!request.ok) { | ||
349 | throw request; | ||
350 | } | ||
351 | const data = await request.json(); | ||
352 | |||
353 | console.debug('ServerApi::getPaymentDashboardUrl resolves', data); | ||
354 | return data; | ||
355 | } | ||
356 | |||
357 | async getSubscriptionOrders() { | ||
358 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me/subscription`, this._prepareAuthRequest({ | ||
359 | method: 'GET', | ||
360 | })); | ||
361 | if (!request.ok) { | ||
362 | throw request; | ||
363 | } | ||
364 | const data = await request.json(); | ||
365 | const orders = this._mapOrderModels(data); | ||
366 | console.debug('ServerApi::getSubscriptionOrders resolves', orders); | ||
367 | return orders; | ||
368 | } | ||
369 | |||
370 | // News | ||
371 | async getLatestNews() { | ||
372 | // eslint-disable-next-line | ||
373 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/news?platform=${os.platform()}&arch=${os.arch()}version=${app.getVersion()}`, | ||
374 | this._prepareAuthRequest({ | ||
375 | method: 'GET', | ||
376 | })); | ||
377 | |||
378 | if (!request.ok) { | ||
379 | throw request; | ||
380 | } | ||
381 | const data = await request.json(); | ||
382 | const news = this._mapNewsModels(data); | ||
383 | console.debug('ServerApi::getLatestNews resolves', news); | ||
384 | return news; | ||
385 | } | ||
386 | |||
387 | async hideNews(id) { | ||
388 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/news/${id}/read`, | ||
389 | this._prepareAuthRequest({ | ||
390 | method: 'GET', | ||
391 | })); | ||
392 | |||
393 | if (!request.ok) { | ||
394 | throw request; | ||
395 | } | ||
396 | |||
397 | console.debug('ServerApi::hideNews resolves', id); | ||
398 | } | ||
399 | |||
400 | // Health Check | ||
401 | async healthCheck() { | ||
402 | const request = await window.fetch(`${SERVER_URL}/health`, this._prepareAuthRequest({ | ||
403 | method: 'GET', | ||
404 | }, false)); | ||
405 | if (!request.ok) { | ||
406 | throw request; | ||
407 | } | ||
408 | console.debug('ServerApi::healthCheck resolves'); | ||
409 | } | ||
410 | |||
411 | async getLegacyServices() { | ||
412 | const file = path.join(app.getPath('userData'), 'settings', 'services.json'); | ||
413 | |||
414 | try { | ||
415 | const config = fs.readJsonSync(file); | ||
416 | |||
417 | if (Object.prototype.hasOwnProperty.call(config, 'services')) { | ||
418 | const services = await Promise.all(config.services.map(async (s) => { | ||
419 | const service = s; | ||
420 | const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/recipes/${s.service}`, | ||
421 | this._prepareAuthRequest({ | ||
422 | method: 'GET', | ||
423 | }), | ||
424 | ); | ||
425 | |||
426 | if (request.status === 200) { | ||
427 | const data = await request.json(); | ||
428 | service.recipe = new RecipePreviewModel(data); | ||
429 | } | ||
430 | |||
431 | return service; | ||
432 | })); | ||
433 | |||
434 | console.debug('ServerApi::getLegacyServices resolves', services); | ||
435 | return services; | ||
436 | } | ||
437 | } catch (err) { | ||
438 | throw (new Error('ServerApi::getLegacyServices no config found')); | ||
439 | } | ||
440 | |||
441 | return []; | ||
442 | } | ||
443 | |||
444 | // Helper | ||
445 | async _mapServiceModels(services) { | ||
446 | return Promise.all(services | ||
447 | .map(async service => await this._prepareServiceModel(service)) // eslint-disable-line | ||
448 | ); | ||
449 | } | ||
450 | |||
451 | async _prepareServiceModel(service) { | ||
452 | let recipe; | ||
453 | try { | ||
454 | recipe = this.recipes.find(r => r.id === service.recipeId); | ||
455 | |||
456 | if (!recipe) { | ||
457 | console.warn(`Recipe '${service.recipeId}' not installed, trying to fetch from server`); | ||
458 | |||
459 | await this.getRecipePackage(service.recipeId); | ||
460 | |||
461 | console.debug('Rerun ServerAPI::getInstalledRecipes'); | ||
462 | await this.getInstalledRecipes(); | ||
463 | |||
464 | recipe = this.recipes.find(r => r.id === service.recipeId); | ||
465 | |||
466 | if (!recipe) { | ||
467 | console.warn(`Could not load recipe ${service.recipeId}`); | ||
468 | return null; | ||
469 | } | ||
470 | } | ||
471 | |||
472 | return new ServiceModel(service, recipe); | ||
473 | } catch (e) { | ||
474 | console.debug(e); | ||
475 | return null; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | _mapRecipePreviewModel(recipes) { | ||
480 | return recipes.map((recipe) => { | ||
481 | try { | ||
482 | return new RecipePreviewModel(recipe); | ||
483 | } catch (e) { | ||
484 | console.error(e); | ||
485 | return null; | ||
486 | } | ||
487 | }).filter(recipe => recipe !== null); | ||
488 | } | ||
489 | |||
490 | _mapNewsModels(news) { | ||
491 | return news.map((newsItem) => { | ||
492 | try { | ||
493 | return new NewsModel(newsItem); | ||
494 | } catch (e) { | ||
495 | console.error(e); | ||
496 | return null; | ||
497 | } | ||
498 | }).filter(newsItem => newsItem !== null); | ||
499 | } | ||
500 | |||
501 | _mapOrderModels(orders) { | ||
502 | return orders.map((orderItem) => { | ||
503 | try { | ||
504 | return new OrderModel(orderItem); | ||
505 | } catch (e) { | ||
506 | console.error(e); | ||
507 | return null; | ||
508 | } | ||
509 | }).filter(orderItem => orderItem !== null); | ||
510 | } | ||
511 | |||
512 | _prepareAuthRequest(options, auth = true) { | ||
513 | const request = Object.assign(options, { | ||
514 | mode: 'cors', | ||
515 | headers: { | ||
516 | 'Content-Type': 'application/json', | ||
517 | 'X-Franz-Source': 'desktop', | ||
518 | 'X-Franz-Version': app.getVersion(), | ||
519 | 'X-Franz-platform': process.platform, | ||
520 | 'X-Franz-Timezone-Offset': new Date().getTimezoneOffset(), | ||
521 | 'X-Franz-System-Locale': app.getLocale(), | ||
522 | }, | ||
523 | }); | ||
524 | |||
525 | // const headers = new window.Headers(); | ||
526 | // headers.append('foo', 'bar'); | ||
527 | // console.log(headers, request.headers); | ||
528 | // | ||
529 | // | ||
530 | // // request.headers.map((value, header) => headers.append(header, value)); | ||
531 | // Object.keys(request.headers).map((key) => { | ||
532 | // console.log(key); | ||
533 | // return headers.append(key, request.headers[key]); | ||
534 | // }); | ||
535 | // request.headers = headers; | ||
536 | |||
537 | // console.log(request); | ||
538 | |||
539 | if (auth) { | ||
540 | request.headers.Authorization = `Bearer ${localStorage.getItem('authToken')}`; | ||
541 | } | ||
542 | |||
543 | return request; | ||
544 | } | ||
545 | |||
546 | _getDevRecipes() { | ||
547 | const recipesDirectory = getDevRecipeDirectory(); | ||
548 | try { | ||
549 | const paths = fs.readdirSync(recipesDirectory) | ||
550 | .filter(file => fs.statSync(path.join(recipesDirectory, file)).isDirectory() && file !== 'temp'); | ||
551 | |||
552 | const recipes = paths.map((id) => { | ||
553 | // eslint-disable-next-line | ||
554 | const Recipe = require(id)(RecipeModel); | ||
555 | return new Recipe(loadRecipeConfig(id)); | ||
556 | }).filter(recipe => recipe.id).map((data) => { | ||
557 | const recipe = data; | ||
558 | |||
559 | recipe.icons = { | ||
560 | svg: `${recipe.path}/icon.svg`, | ||
561 | png: `${recipe.path}/icon.png`, | ||
562 | }; | ||
563 | recipe.local = true; | ||
564 | |||
565 | return data; | ||
566 | }); | ||
567 | |||
568 | return recipes; | ||
569 | } catch (err) { | ||
570 | console.debug('Folder `recipe/dev` does not exist'); | ||
571 | return false; | ||
572 | } | ||
573 | } | ||
574 | } | ||