diff options
author | MCMXC <16797721+mcmxcdev@users.noreply.github.com> | 2024-02-10 18:37:40 -0700 |
---|---|---|
committer | MCMXC <16797721+mcmxcdev@users.noreply.github.com> | 2024-02-10 18:37:40 -0700 |
commit | 2ad43db1ed01fe0b5b116ddda1a5177c9f62c130 (patch) | |
tree | 8a6c6b551e7216d40c86e0954d828c783848a37b /app/Controllers/Http/ServiceController.ts | |
parent | refactor: project maintenance (diff) | |
download | ferdium-server-2ad43db1ed01fe0b5b116ddda1a5177c9f62c130.tar.gz ferdium-server-2ad43db1ed01fe0b5b116ddda1a5177c9f62c130.tar.zst ferdium-server-2ad43db1ed01fe0b5b116ddda1a5177c9f62c130.zip |
updates
Diffstat (limited to 'app/Controllers/Http/ServiceController.ts')
-rw-r--r-- | app/Controllers/Http/ServiceController.ts | 173 |
1 files changed, 94 insertions, 79 deletions
diff --git a/app/Controllers/Http/ServiceController.ts b/app/Controllers/Http/ServiceController.ts index 9988244..8fec844 100644 --- a/app/Controllers/Http/ServiceController.ts +++ b/app/Controllers/Http/ServiceController.ts | |||
@@ -1,49 +1,49 @@ | |||
1 | import type { HttpContext } from '@adonisjs/core/http' | 1 | import type { HttpContext } from '@adonisjs/core/http'; |
2 | import { schema } from '@adonisjs/validator' | 2 | import { schema } from '@adonisjs/validator'; |
3 | import Service from '#app/Models/Service' | 3 | import Service from '#app/Models/Service'; |
4 | import { url } from '#config/app' | 4 | import { url } from '#config/app'; |
5 | import { v4 as uuid } from 'uuid' | 5 | import { v4 as uuid } from 'uuid'; |
6 | import * as fs from 'fs-extra' | 6 | import * as fs from 'fs-extra'; |
7 | import path from 'node:path' | 7 | import path from 'node:path'; |
8 | import { app } from '@adonisjs/core/services/app' | 8 | import { app } from '@adonisjs/core/services/app'; |
9 | import sanitize from 'sanitize-filename' | 9 | import sanitize from 'sanitize-filename'; |
10 | 10 | ||
11 | const createSchema = schema.create({ | 11 | const createSchema = schema.create({ |
12 | name: schema.string(), | 12 | name: schema.string(), |
13 | recipeId: schema.string(), | 13 | recipeId: schema.string(), |
14 | }) | 14 | }); |
15 | 15 | ||
16 | export default class ServiceController { | 16 | export default class ServiceController { |
17 | // Create a new service for user | 17 | // Create a new service for user |
18 | public async create({ request, response, auth }: HttpContext) { | 18 | public async create({ request, response, auth }: HttpContext) { |
19 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. | 19 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. |
20 | const user = auth.user ?? request.user | 20 | const user = auth.user ?? request.user; |
21 | 21 | ||
22 | if (!user) { | 22 | if (!user) { |
23 | return response.unauthorized('Missing or invalid api token') | 23 | return response.unauthorized('Missing or invalid api token'); |
24 | } | 24 | } |
25 | 25 | ||
26 | // Validate user input | 26 | // Validate user input |
27 | const data = request.all() | 27 | const data = request.all(); |
28 | 28 | ||
29 | try { | 29 | try { |
30 | await request.validate({ schema: createSchema }) | 30 | await request.validate({ schema: createSchema }); |
31 | } catch (error) { | 31 | } catch (error) { |
32 | return response.status(401).send({ | 32 | return response.status(401).send({ |
33 | message: 'Invalid POST arguments', | 33 | message: 'Invalid POST arguments', |
34 | messages: error.messages, | 34 | messages: error.messages, |
35 | status: 401, | 35 | status: 401, |
36 | }) | 36 | }); |
37 | } | 37 | } |
38 | 38 | ||
39 | // Get new, unused uuid | 39 | // Get new, unused uuid |
40 | let serviceId | 40 | let serviceId; |
41 | do { | 41 | do { |
42 | serviceId = uuid() | 42 | serviceId = uuid(); |
43 | } while ( | 43 | } while ( |
44 | // eslint-disable-next-line no-await-in-loop, unicorn/no-await-expression-member | 44 | // eslint-disable-next-line no-await-in-loop, unicorn/no-await-expression-member |
45 | (await Service.query().where('serviceId', serviceId)).length > 0 | 45 | (await Service.query().where('serviceId', serviceId)).length > 0 |
46 | ) | 46 | ); |
47 | 47 | ||
48 | await Service.create({ | 48 | await Service.create({ |
49 | userId: user.id, | 49 | userId: user.id, |
@@ -51,7 +51,7 @@ export default class ServiceController { | |||
51 | name: data.name, | 51 | name: data.name, |
52 | recipeId: data.recipeId, | 52 | recipeId: data.recipeId, |
53 | settings: JSON.stringify(data), | 53 | settings: JSON.stringify(data), |
54 | }) | 54 | }); |
55 | 55 | ||
56 | return response.send({ | 56 | return response.send({ |
57 | data: { | 57 | data: { |
@@ -72,26 +72,28 @@ export default class ServiceController { | |||
72 | ...data, | 72 | ...data, |
73 | }, | 73 | }, |
74 | status: ['created'], | 74 | status: ['created'], |
75 | }) | 75 | }); |
76 | } | 76 | } |
77 | 77 | ||
78 | // List all services a user has created | 78 | // List all services a user has created |
79 | public async list({ request, response, auth }: HttpContext) { | 79 | public async list({ request, response, auth }: HttpContext) { |
80 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. | 80 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. |
81 | const user = auth.user ?? request.user | 81 | const user = auth.user ?? request.user; |
82 | 82 | ||
83 | if (!user) { | 83 | if (!user) { |
84 | return response.unauthorized('Missing or invalid api token') | 84 | return response.unauthorized('Missing or invalid api token'); |
85 | } | 85 | } |
86 | 86 | ||
87 | const { id } = user | 87 | const { id } = user; |
88 | const services = await user.related('services').query() | 88 | const services = await user.related('services').query(); |
89 | 89 | ||
90 | // Convert to array with all data Franz wants | 90 | // Convert to array with all data Franz wants |
91 | // eslint-disable-next-line @typescript-eslint/no-explicit-any | 91 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
92 | const servicesArray = services.map((service: any) => { | 92 | const servicesArray = services.map((service: any) => { |
93 | const settings = | 93 | const settings = |
94 | typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings | 94 | typeof service.settings === 'string' |
95 | ? JSON.parse(service.settings) | ||
96 | : service.settings; | ||
95 | 97 | ||
96 | return { | 98 | return { |
97 | customRecipe: false, | 99 | customRecipe: false, |
@@ -113,82 +115,89 @@ export default class ServiceController { | |||
113 | name: service.name, | 115 | name: service.name, |
114 | recipeId: service.recipeId, | 116 | recipeId: service.recipeId, |
115 | userId: id, | 117 | userId: id, |
116 | } | 118 | }; |
117 | }) | 119 | }); |
118 | 120 | ||
119 | return response.send(servicesArray) | 121 | return response.send(servicesArray); |
120 | } | 122 | } |
121 | 123 | ||
122 | public async delete({ request, params, auth, response }: HttpContext) { | 124 | public async delete({ request, params, auth, response }: HttpContext) { |
123 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. | 125 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. |
124 | const user = auth.user ?? request.user | 126 | const user = auth.user ?? request.user; |
125 | 127 | ||
126 | if (!user) { | 128 | if (!user) { |
127 | return response.unauthorized('Missing or invalid api token') | 129 | return response.unauthorized('Missing or invalid api token'); |
128 | } | 130 | } |
129 | 131 | ||
130 | // Update data in database | 132 | // Update data in database |
131 | await Service.query().where('serviceId', params.id).where('userId', user.id).delete() | 133 | await Service.query() |
134 | .where('serviceId', params.id) | ||
135 | .where('userId', user.id) | ||
136 | .delete(); | ||
132 | 137 | ||
133 | return response.send({ | 138 | return response.send({ |
134 | message: 'Sucessfully deleted service', | 139 | message: 'Sucessfully deleted service', |
135 | status: 200, | 140 | status: 200, |
136 | }) | 141 | }); |
137 | } | 142 | } |
138 | 143 | ||
139 | // TODO: Test if icon upload works | 144 | // TODO: Test if icon upload works |
140 | public async edit({ request, response, auth, params }: HttpContext) { | 145 | public async edit({ request, response, auth, params }: HttpContext) { |
141 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. | 146 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. |
142 | const user = auth.user ?? request.user | 147 | const user = auth.user ?? request.user; |
143 | 148 | ||
144 | if (!user) { | 149 | if (!user) { |
145 | return response.unauthorized('Missing or invalid api token') | 150 | return response.unauthorized('Missing or invalid api token'); |
146 | } | 151 | } |
147 | 152 | ||
148 | const { id } = params | 153 | const { id } = params; |
149 | const service = await Service.query() | 154 | const service = await Service.query() |
150 | .where('serviceId', id) | 155 | .where('serviceId', id) |
151 | .where('userId', user.id) | 156 | .where('userId', user.id) |
152 | .firstOrFail() | 157 | .firstOrFail(); |
153 | 158 | ||
154 | if (request.file('icon')) { | 159 | if (request.file('icon')) { |
155 | // Upload custom service icon | 160 | // Upload custom service icon |
156 | const icon = request.file('icon', { | 161 | const icon = request.file('icon', { |
157 | extnames: ['png', 'jpg', 'jpeg', 'svg'], | 162 | extnames: ['png', 'jpg', 'jpeg', 'svg'], |
158 | size: '2mb', | 163 | size: '2mb', |
159 | }) | 164 | }); |
160 | 165 | ||
161 | if (icon === null) { | 166 | if (icon === null) { |
162 | return response.badRequest('Icon not uploaded.') | 167 | return response.badRequest('Icon not uploaded.'); |
163 | } | 168 | } |
164 | 169 | ||
165 | const settings = | 170 | const settings = |
166 | typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings | 171 | typeof service.settings === 'string' |
172 | ? JSON.parse(service.settings) | ||
173 | : service.settings; | ||
167 | 174 | ||
168 | let iconId | 175 | let iconId; |
169 | do { | 176 | do { |
170 | iconId = uuid() + uuid() | 177 | iconId = uuid() + uuid(); |
171 | } while ( | 178 | } while ( |
172 | // eslint-disable-next-line no-await-in-loop | 179 | // eslint-disable-next-line no-await-in-loop |
173 | await fs.exists(path.join(app.tmpPath('uploads'), iconId)) | 180 | await fs.exists(path.join(app.tmpPath('uploads'), iconId)) |
174 | ) | 181 | ); |
175 | iconId = `${iconId}.${icon.extname}` | 182 | iconId = `${iconId}.${icon.extname}`; |
176 | 183 | ||
177 | await icon.move(app.tmpPath('uploads'), { | 184 | await icon.move(app.tmpPath('uploads'), { |
178 | name: iconId, | 185 | name: iconId, |
179 | overwrite: true, | 186 | overwrite: true, |
180 | }) | 187 | }); |
181 | 188 | ||
182 | if (icon.state !== 'moved') { | 189 | if (icon.state !== 'moved') { |
183 | return response.status(500).send(icon.errors) | 190 | return response.status(500).send(icon.errors); |
184 | } | 191 | } |
185 | 192 | ||
186 | const newSettings = { | 193 | const newSettings = { |
187 | ...settings, | 194 | ...settings, |
188 | 195 | ||
189 | iconId, | 196 | iconId, |
190 | customIconVersion: settings?.customIconVersion ? settings.customIconVersion + 1 : 1, | 197 | customIconVersion: settings?.customIconVersion |
191 | } | 198 | ? settings.customIconVersion + 1 |
199 | : 1, | ||
200 | }; | ||
192 | 201 | ||
193 | // Update data in database | 202 | // Update data in database |
194 | await Service.query() | 203 | await Service.query() |
@@ -197,7 +206,7 @@ export default class ServiceController { | |||
197 | .update({ | 206 | .update({ |
198 | name: service.name, | 207 | name: service.name, |
199 | settings: JSON.stringify(newSettings), | 208 | settings: JSON.stringify(newSettings), |
200 | }) | 209 | }); |
201 | 210 | ||
202 | return response.send({ | 211 | return response.send({ |
203 | data: { | 212 | data: { |
@@ -208,24 +217,28 @@ export default class ServiceController { | |||
208 | userId: user.id, | 217 | userId: user.id, |
209 | }, | 218 | }, |
210 | status: ['updated'], | 219 | status: ['updated'], |
211 | }) | 220 | }); |
212 | } | 221 | } |
213 | // Update service info | 222 | // Update service info |
214 | const data = request.all() | 223 | const data = request.all(); |
215 | 224 | ||
216 | const settings = { | 225 | const settings = { |
217 | ...(typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings), | 226 | ...(typeof service.settings === 'string' |
227 | ? JSON.parse(service.settings) | ||
228 | : service.settings), | ||
218 | ...data, | 229 | ...data, |
219 | } | 230 | }; |
220 | 231 | ||
221 | if (settings.customIcon === 'delete') { | 232 | if (settings.customIcon === 'delete') { |
222 | fs.remove(path.join(app.tmpPath('uploads'), settings.iconId)).catch((error) => { | 233 | fs.remove(path.join(app.tmpPath('uploads'), settings.iconId)).catch( |
223 | console.error(error) | 234 | error => { |
224 | }) | 235 | console.error(error); |
236 | }, | ||
237 | ); | ||
225 | 238 | ||
226 | settings.iconId = undefined | 239 | settings.iconId = undefined; |
227 | settings.customIconVersion = undefined | 240 | settings.customIconVersion = undefined; |
228 | settings.customIcon = '' | 241 | settings.customIcon = ''; |
229 | } | 242 | } |
230 | 243 | ||
231 | // Update data in database | 244 | // Update data in database |
@@ -235,13 +248,13 @@ export default class ServiceController { | |||
235 | .update({ | 248 | .update({ |
236 | name: data.name, | 249 | name: data.name, |
237 | settings: JSON.stringify(settings), | 250 | settings: JSON.stringify(settings), |
238 | }) | 251 | }); |
239 | 252 | ||
240 | // Get updated row | 253 | // Get updated row |
241 | const serviceUpdated = await Service.query() | 254 | const serviceUpdated = await Service.query() |
242 | .where('serviceId', id) | 255 | .where('serviceId', id) |
243 | .where('userId', user.id) | 256 | .where('userId', user.id) |
244 | .firstOrFail() | 257 | .firstOrFail(); |
245 | 258 | ||
246 | return response.send({ | 259 | return response.send({ |
247 | data: { | 260 | data: { |
@@ -252,19 +265,19 @@ export default class ServiceController { | |||
252 | userId: user.id, | 265 | userId: user.id, |
253 | }, | 266 | }, |
254 | status: ['updated'], | 267 | status: ['updated'], |
255 | }) | 268 | }); |
256 | } | 269 | } |
257 | 270 | ||
258 | // TODO: Test if this works | 271 | // TODO: Test if this works |
259 | public async reorder({ request, response, auth }: HttpContext) { | 272 | public async reorder({ request, response, auth }: HttpContext) { |
260 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. | 273 | // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. |
261 | const user = auth.user ?? request.user | 274 | const user = auth.user ?? request.user; |
262 | 275 | ||
263 | if (!user) { | 276 | if (!user) { |
264 | return response.unauthorized('Missing or invalid api token') | 277 | return response.unauthorized('Missing or invalid api token'); |
265 | } | 278 | } |
266 | 279 | ||
267 | const data = request.all() | 280 | const data = request.all(); |
268 | 281 | ||
269 | for (const service of Object.keys(data)) { | 282 | for (const service of Object.keys(data)) { |
270 | // Get current settings from db | 283 | // Get current settings from db |
@@ -272,14 +285,14 @@ export default class ServiceController { | |||
272 | .where('serviceId', service) | 285 | .where('serviceId', service) |
273 | .where('userId', user.id) | 286 | .where('userId', user.id) |
274 | 287 | ||
275 | .firstOrFail() | 288 | .firstOrFail(); |
276 | 289 | ||
277 | const settings = { | 290 | const settings = { |
278 | ...(typeof serviceData.settings === 'string' | 291 | ...(typeof serviceData.settings === 'string' |
279 | ? JSON.parse(serviceData.settings) | 292 | ? JSON.parse(serviceData.settings) |
280 | : serviceData.settings), | 293 | : serviceData.settings), |
281 | order: data[service], | 294 | order: data[service], |
282 | } | 295 | }; |
283 | 296 | ||
284 | // Update data in database | 297 | // Update data in database |
285 | await Service.query() // eslint-disable-line no-await-in-loop | 298 | await Service.query() // eslint-disable-line no-await-in-loop |
@@ -287,16 +300,18 @@ export default class ServiceController { | |||
287 | .where('userId', user.id) | 300 | .where('userId', user.id) |
288 | .update({ | 301 | .update({ |
289 | settings: JSON.stringify(settings), | 302 | settings: JSON.stringify(settings), |
290 | }) | 303 | }); |
291 | } | 304 | } |
292 | 305 | ||
293 | // Get new services | 306 | // Get new services |
294 | const services = await user.related('services').query() | 307 | const services = await user.related('services').query(); |
295 | // Convert to array with all data Franz wants | 308 | // Convert to array with all data Franz wants |
296 | // eslint-disable-next-line @typescript-eslint/no-explicit-any | 309 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
297 | const servicesArray = services.map((service: any) => { | 310 | const servicesArray = services.map((service: any) => { |
298 | const settings = | 311 | const settings = |
299 | typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings | 312 | typeof service.settings === 'string' |
313 | ? JSON.parse(service.settings) | ||
314 | : service.settings; | ||
300 | 315 | ||
301 | return { | 316 | return { |
302 | customRecipe: false, | 317 | customRecipe: false, |
@@ -318,34 +333,34 @@ export default class ServiceController { | |||
318 | name: service.name, | 333 | name: service.name, |
319 | recipeId: service.recipeId, | 334 | recipeId: service.recipeId, |
320 | userId: user.id, | 335 | userId: user.id, |
321 | } | 336 | }; |
322 | }) | 337 | }); |
323 | 338 | ||
324 | return response.send(servicesArray) | 339 | return response.send(servicesArray); |
325 | } | 340 | } |
326 | 341 | ||
327 | // TODO: Test if this works | 342 | // TODO: Test if this works |
328 | public async icon({ params, response }: HttpContext) { | 343 | public async icon({ params, response }: HttpContext) { |
329 | let { id } = params | 344 | let { id } = params; |
330 | 345 | ||
331 | id = sanitize(id) | 346 | id = sanitize(id); |
332 | if (id === '') { | 347 | if (id === '') { |
333 | return response.status(404).send({ | 348 | return response.status(404).send({ |
334 | status: "Icon doesn't exist", | 349 | status: "Icon doesn't exist", |
335 | }) | 350 | }); |
336 | } | 351 | } |
337 | 352 | ||
338 | const iconPath = path.join(app.tmpPath('uploads'), id) | 353 | const iconPath = path.join(app.tmpPath('uploads'), id); |
339 | 354 | ||
340 | try { | 355 | try { |
341 | await fs.access(iconPath) | 356 | await fs.access(iconPath); |
342 | } catch { | 357 | } catch { |
343 | // File not available. | 358 | // File not available. |
344 | return response.status(404).send({ | 359 | return response.status(404).send({ |
345 | status: "Icon doesn't exist", | 360 | status: "Icon doesn't exist", |
346 | }) | 361 | }); |
347 | } | 362 | } |
348 | 363 | ||
349 | return response.download(iconPath) | 364 | return response.download(iconPath); |
350 | } | 365 | } |
351 | } | 366 | } |