aboutsummaryrefslogtreecommitdiffstats
path: root/app/Controllers/Http/ServiceController.ts
diff options
context:
space:
mode:
Diffstat (limited to 'app/Controllers/Http/ServiceController.ts')
-rw-r--r--app/Controllers/Http/ServiceController.ts204
1 files changed, 92 insertions, 112 deletions
diff --git a/app/Controllers/Http/ServiceController.ts b/app/Controllers/Http/ServiceController.ts
index 76e72e4..9988244 100644
--- a/app/Controllers/Http/ServiceController.ts
+++ b/app/Controllers/Http/ServiceController.ts
@@ -1,49 +1,49 @@
1import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; 1import type { HttpContext } from '@adonisjs/core/http'
2import { schema } from '@ioc:Adonis/Core/Validator'; 2import { schema } from '@adonisjs/validator'
3import Service from 'App/Models/Service'; 3import Service from '#app/Models/Service'
4import { url } from 'Config/app'; 4import { url } from '#config/app'
5import { v4 as uuid } from 'uuid'; 5import { v4 as uuid } from 'uuid'
6import * as fs from 'fs-extra'; 6import * as fs from 'fs-extra'
7import path from 'node:path'; 7import path from 'node:path'
8import Application from '@ioc:Adonis/Core/Application'; 8import { app } from '@adonisjs/core/services/app'
9import sanitize from 'sanitize-filename'; 9import sanitize from 'sanitize-filename'
10 10
11const createSchema = schema.create({ 11const createSchema = schema.create({
12 name: schema.string(), 12 name: schema.string(),
13 recipeId: schema.string(), 13 recipeId: schema.string(),
14}); 14})
15 15
16export default class ServiceController { 16export default class ServiceController {
17 // Create a new service for user 17 // Create a new service for user
18 public async create({ request, response, auth }: HttpContextContract) { 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,28 +72,26 @@ 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 }: HttpContextContract) { 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' 94 typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings
95 ? JSON.parse(service.settings)
96 : service.settings;
97 95
98 return { 96 return {
99 customRecipe: false, 97 customRecipe: false,
@@ -110,99 +108,87 @@ export default class ServiceController {
110 iconUrl: settings.iconId 108 iconUrl: settings.iconId
111 ? `${url}/v1/icon/${settings.iconId}` 109 ? `${url}/v1/icon/${settings.iconId}`
112 : // eslint-disable-next-line unicorn/no-null 110 : // eslint-disable-next-line unicorn/no-null
113 null, 111 null,
114 id: service.serviceId, 112 id: service.serviceId,
115 name: service.name, 113 name: service.name,
116 recipeId: service.recipeId, 114 recipeId: service.recipeId,
117 userId: id, 115 userId: id,
118 }; 116 }
119 }); 117 })
120 118
121 return response.send(servicesArray); 119 return response.send(servicesArray)
122 } 120 }
123 121
124 public async delete({ 122 public async delete({ request, params, auth, response }: HttpContext) {
125 request,
126 params,
127 auth,
128 response,
129 }: HttpContextContract) {
130 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. 123 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'.
131 const user = auth.user ?? request.user; 124 const user = auth.user ?? request.user
132 125
133 if (!user) { 126 if (!user) {
134 return response.unauthorized('Missing or invalid api token'); 127 return response.unauthorized('Missing or invalid api token')
135 } 128 }
136 129
137 // Update data in database 130 // Update data in database
138 await Service.query() 131 await Service.query().where('serviceId', params.id).where('userId', user.id).delete()
139 .where('serviceId', params.id)
140 .where('userId', user.id)
141 .delete();
142 132
143 return response.send({ 133 return response.send({
144 message: 'Sucessfully deleted service', 134 message: 'Sucessfully deleted service',
145 status: 200, 135 status: 200,
146 }); 136 })
147 } 137 }
148 138
149 // TODO: Test if icon upload works 139 // TODO: Test if icon upload works
150 public async edit({ request, response, auth, params }: HttpContextContract) { 140 public async edit({ request, response, auth, params }: HttpContext) {
151 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. 141 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'.
152 const user = auth.user ?? request.user; 142 const user = auth.user ?? request.user
153 143
154 if (!user) { 144 if (!user) {
155 return response.unauthorized('Missing or invalid api token'); 145 return response.unauthorized('Missing or invalid api token')
156 } 146 }
157 147
158 const { id } = params; 148 const { id } = params
159 const service = await Service.query() 149 const service = await Service.query()
160 .where('serviceId', id) 150 .where('serviceId', id)
161 .where('userId', user.id) 151 .where('userId', user.id)
162 .firstOrFail(); 152 .firstOrFail()
163 153
164 if (request.file('icon')) { 154 if (request.file('icon')) {
165 // Upload custom service icon 155 // Upload custom service icon
166 const icon = request.file('icon', { 156 const icon = request.file('icon', {
167 extnames: ['png', 'jpg', 'jpeg', 'svg'], 157 extnames: ['png', 'jpg', 'jpeg', 'svg'],
168 size: '2mb', 158 size: '2mb',
169 }); 159 })
170 160
171 if (icon === null) { 161 if (icon === null) {
172 return response.badRequest('Icon not uploaded.'); 162 return response.badRequest('Icon not uploaded.')
173 } 163 }
174 164
175 const settings = 165 const settings =
176 typeof service.settings === 'string' 166 typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings
177 ? JSON.parse(service.settings)
178 : service.settings;
179 167
180 let iconId; 168 let iconId
181 do { 169 do {
182 iconId = uuid() + uuid(); 170 iconId = uuid() + uuid()
183 } while ( 171 } while (
184 // eslint-disable-next-line no-await-in-loop 172 // eslint-disable-next-line no-await-in-loop
185 await fs.exists(path.join(Application.tmpPath('uploads'), iconId)) 173 await fs.exists(path.join(app.tmpPath('uploads'), iconId))
186 ); 174 )
187 iconId = `${iconId}.${icon.extname}`; 175 iconId = `${iconId}.${icon.extname}`
188 176
189 await icon.move(Application.tmpPath('uploads'), { 177 await icon.move(app.tmpPath('uploads'), {
190 name: iconId, 178 name: iconId,
191 overwrite: true, 179 overwrite: true,
192 }); 180 })
193 181
194 if (icon.state !== 'moved') { 182 if (icon.state !== 'moved') {
195 return response.status(500).send(icon.errors); 183 return response.status(500).send(icon.errors)
196 } 184 }
197 185
198 const newSettings = { 186 const newSettings = {
199 ...settings, 187 ...settings,
200 188
201 iconId, 189 iconId,
202 customIconVersion: settings?.customIconVersion 190 customIconVersion: settings?.customIconVersion ? settings.customIconVersion + 1 : 1,
203 ? settings.customIconVersion + 1 191 }
204 : 1,
205 };
206 192
207 // Update data in database 193 // Update data in database
208 await Service.query() 194 await Service.query()
@@ -211,7 +197,7 @@ export default class ServiceController {
211 .update({ 197 .update({
212 name: service.name, 198 name: service.name,
213 settings: JSON.stringify(newSettings), 199 settings: JSON.stringify(newSettings),
214 }); 200 })
215 201
216 return response.send({ 202 return response.send({
217 data: { 203 data: {
@@ -222,28 +208,24 @@ export default class ServiceController {
222 userId: user.id, 208 userId: user.id,
223 }, 209 },
224 status: ['updated'], 210 status: ['updated'],
225 }); 211 })
226 } 212 }
227 // Update service info 213 // Update service info
228 const data = request.all(); 214 const data = request.all()
229 215
230 const settings = { 216 const settings = {
231 ...(typeof service.settings === 'string' 217 ...(typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings),
232 ? JSON.parse(service.settings)
233 : service.settings),
234 ...data, 218 ...data,
235 }; 219 }
236 220
237 if (settings.customIcon === 'delete') { 221 if (settings.customIcon === 'delete') {
238 fs.remove( 222 fs.remove(path.join(app.tmpPath('uploads'), settings.iconId)).catch((error) => {
239 path.join(Application.tmpPath('uploads'), settings.iconId), 223 console.error(error)
240 ).catch(error => { 224 })
241 console.error(error); 225
242 }); 226 settings.iconId = undefined
243 227 settings.customIconVersion = undefined
244 settings.iconId = undefined; 228 settings.customIcon = ''
245 settings.customIconVersion = undefined;
246 settings.customIcon = '';
247 } 229 }
248 230
249 // Update data in database 231 // Update data in database
@@ -253,13 +235,13 @@ export default class ServiceController {
253 .update({ 235 .update({
254 name: data.name, 236 name: data.name,
255 settings: JSON.stringify(settings), 237 settings: JSON.stringify(settings),
256 }); 238 })
257 239
258 // Get updated row 240 // Get updated row
259 const serviceUpdated = await Service.query() 241 const serviceUpdated = await Service.query()
260 .where('serviceId', id) 242 .where('serviceId', id)
261 .where('userId', user.id) 243 .where('userId', user.id)
262 .firstOrFail(); 244 .firstOrFail()
263 245
264 return response.send({ 246 return response.send({
265 data: { 247 data: {
@@ -270,19 +252,19 @@ export default class ServiceController {
270 userId: user.id, 252 userId: user.id,
271 }, 253 },
272 status: ['updated'], 254 status: ['updated'],
273 }); 255 })
274 } 256 }
275 257
276 // TODO: Test if this works 258 // TODO: Test if this works
277 public async reorder({ request, response, auth }: HttpContextContract) { 259 public async reorder({ request, response, auth }: HttpContext) {
278 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'. 260 // @ts-expect-error Property 'user' does not exist on type 'HttpContextContract'.
279 const user = auth.user ?? request.user; 261 const user = auth.user ?? request.user
280 262
281 if (!user) { 263 if (!user) {
282 return response.unauthorized('Missing or invalid api token'); 264 return response.unauthorized('Missing or invalid api token')
283 } 265 }
284 266
285 const data = request.all(); 267 const data = request.all()
286 268
287 for (const service of Object.keys(data)) { 269 for (const service of Object.keys(data)) {
288 // Get current settings from db 270 // Get current settings from db
@@ -290,14 +272,14 @@ export default class ServiceController {
290 .where('serviceId', service) 272 .where('serviceId', service)
291 .where('userId', user.id) 273 .where('userId', user.id)
292 274
293 .firstOrFail(); 275 .firstOrFail()
294 276
295 const settings = { 277 const settings = {
296 ...(typeof serviceData.settings === 'string' 278 ...(typeof serviceData.settings === 'string'
297 ? JSON.parse(serviceData.settings) 279 ? JSON.parse(serviceData.settings)
298 : serviceData.settings), 280 : serviceData.settings),
299 order: data[service], 281 order: data[service],
300 }; 282 }
301 283
302 // Update data in database 284 // Update data in database
303 await Service.query() // eslint-disable-line no-await-in-loop 285 await Service.query() // eslint-disable-line no-await-in-loop
@@ -305,18 +287,16 @@ export default class ServiceController {
305 .where('userId', user.id) 287 .where('userId', user.id)
306 .update({ 288 .update({
307 settings: JSON.stringify(settings), 289 settings: JSON.stringify(settings),
308 }); 290 })
309 } 291 }
310 292
311 // Get new services 293 // Get new services
312 const services = await user.related('services').query(); 294 const services = await user.related('services').query()
313 // Convert to array with all data Franz wants 295 // Convert to array with all data Franz wants
314 // eslint-disable-next-line @typescript-eslint/no-explicit-any 296 // eslint-disable-next-line @typescript-eslint/no-explicit-any
315 const servicesArray = services.map((service: any) => { 297 const servicesArray = services.map((service: any) => {
316 const settings = 298 const settings =
317 typeof service.settings === 'string' 299 typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings
318 ? JSON.parse(service.settings)
319 : service.settings;
320 300
321 return { 301 return {
322 customRecipe: false, 302 customRecipe: false,
@@ -333,39 +313,39 @@ export default class ServiceController {
333 iconUrl: settings.iconId 313 iconUrl: settings.iconId
334 ? `${url}/v1/icon/${settings.iconId}` 314 ? `${url}/v1/icon/${settings.iconId}`
335 : // eslint-disable-next-line unicorn/no-null 315 : // eslint-disable-next-line unicorn/no-null
336 null, 316 null,
337 id: service.serviceId, 317 id: service.serviceId,
338 name: service.name, 318 name: service.name,
339 recipeId: service.recipeId, 319 recipeId: service.recipeId,
340 userId: user.id, 320 userId: user.id,
341 }; 321 }
342 }); 322 })
343 323
344 return response.send(servicesArray); 324 return response.send(servicesArray)
345 } 325 }
346 326
347 // TODO: Test if this works 327 // TODO: Test if this works
348 public async icon({ params, response }: HttpContextContract) { 328 public async icon({ params, response }: HttpContext) {
349 let { id } = params; 329 let { id } = params
350 330
351 id = sanitize(id); 331 id = sanitize(id)
352 if (id === '') { 332 if (id === '') {
353 return response.status(404).send({ 333 return response.status(404).send({
354 status: 'Icon doesn\'t exist', 334 status: "Icon doesn't exist",
355 }); 335 })
356 } 336 }
357 337
358 const iconPath = path.join(Application.tmpPath('uploads'), id); 338 const iconPath = path.join(app.tmpPath('uploads'), id)
359 339
360 try { 340 try {
361 await fs.access(iconPath); 341 await fs.access(iconPath)
362 } catch { 342 } catch {
363 // File not available. 343 // File not available.
364 return response.status(404).send({ 344 return response.status(404).send({
365 status: 'Icon doesn\'t exist', 345 status: "Icon doesn't exist",
366 }); 346 })
367 } 347 }
368 348
369 return response.download(iconPath); 349 return response.download(iconPath)
370 } 350 }
371} 351}