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.ts173
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 @@
1import type { HttpContext } from '@adonisjs/core/http' 1import type { HttpContext } from '@adonisjs/core/http';
2import { schema } from '@adonisjs/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 { app } from '@adonisjs/core/services/app' 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 }: 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}