aboutsummaryrefslogtreecommitdiffstats
path: root/app/Controllers/Http/RecipeController.js
diff options
context:
space:
mode:
authorLibravatar Ricardo <ricardo@cino.io>2023-10-13 14:12:03 +0200
committerLibravatar GitHub <noreply@github.com>2023-10-13 13:12:03 +0100
commite503468660a13760010a94ecda5f0625c6f47f87 (patch)
treefa532f54fc5f091de08d55405ec6339bd2440a02 /app/Controllers/Http/RecipeController.js
parent1.3.16 [skip ci] (diff)
downloadferdium-server-e503468660a13760010a94ecda5f0625c6f47f87.tar.gz
ferdium-server-e503468660a13760010a94ecda5f0625c6f47f87.tar.zst
ferdium-server-e503468660a13760010a94ecda5f0625c6f47f87.zip
Server re-build with latest AdonisJS framework & Typescript (#47)
* chore: setup first basis structure * chore: ensure styling is loaded correctly * chore: comply to new routing syntax by replace . with / in routes/resource locations * chore: add login controller * chore: correctly use views with slash instead of dot * chore: working login + tests * chore: clean up tests * chore: add password-forgot endpoint and matching test * chore: add delete page test * chore: add logout test * chore: add reset-password route and tests * chore: remove obsolete comment * chore: add account-page and tests * chore: add data page & first step of the test * chore: add transfer/import data feature and tests * chore: add export and basic test * chore: add all static api routes with tests * Regenerate 'pnpm-lock.json' and fix bad merge conflict WIP: - Tests have been commented out since they dont work - Server doesn't start * easier dev and test runs * - remove --require-pragma from reformat-files so formatting works properly - run pnpm reformat-files over codebase - remove .json files from .eslintignore - add invalid.json file to .eslintignore - configure prettier properly in eslint config - add type jsdoc to prettier config - run adonis generate:manifest command to regenerate ace-manifest.json - specify volta in package.json - introduce typecheck npm script - remove unused .mjs extension from npm scripts - install missing type definition dependencies - add pnpm.allowedDeprecatedVersions to package.json - fix invalid extends in tsconfig.json causing TS issues throughout codebase - remove @ts-ignore throughout codebase which is not relevant anymore - enable some of the tsconfig options - remove outdated eslint-disable from codebase - change deprecated faker.company.companyName() to faker.company.name() - fix TS issues inside transfer.spec.ts * - update to latest node and pnpm versions - upgrade all non-major dependencies to latest - install missing @types/luxon dependency - add cuid to pnpm.allowedDeprecatedVersions - add esModuleInterop config option to tsconfig - migrate more deprecated faker methods to new ones - add more temporary ts-ignore to code * - update eslint config - remove trailingComma: all since default in prettier v3 - add typecheck command to prepare-code npm script - upgrade various dependencies to latest major version - update tsconfig to include only useful config options - disable some lint issues and fix others * - add test command to prepare-code - disable strictPropertyInitialization flag in tsconfig which creates issues with adonis models - update precommit hook to excute pnpm prepare-code - remove ts-ignore statements from all models * fix node and pnpm dependency update * add cross env (so that we can develop on windows) * add signup endpoint (TODO: JWT auth) * Add login endpoint * Add me and updateMe endpoints * Add service endpoint * refactor: change endpoints to use jwt * add recipes endpoint * add workspaces endpoint * fix web controllors for login and post import * Update node deps * Change auth middleware (for web) and exempt api from CSRF * Add import endpoint (franz import) * Fix export/import logic * Fix service and workspace data in user/data * Fix partial lint * chore: workaround lint issues * fix: migration naming had two . * Sync back node with recipes repo * Temporarily ignore typescript * Fix adonisrc to handle public folder static assets * Fix issue with production database * add Legacy Password Provider * Fix lint errors * Fix issue on login errors frontend * add Legacy Password Provider * Fix issue with customIcons * Fix issue with auth tokens * Update 'node' to '18.18.0' * make docker work * improve docker entrypoint (test api performance) * Add migration database script * NODE_ENV on recipes * prefer @ts-expect-error over @ts-ignore * small fixes * Update 'pnpm' to '8.7.6' * fix error catch * Automatically generate JWT Public and Private keys * Use custom Adonis5-jwt * Update code to use secret (old way, no breaking changes) * Normalize appKey * Trick to make JWT tokens on client work with new version * Fix error with new JWT logic * Change migration and how we store JWT * Fix 500 response code (needs to be 401) * Improve logic and fix bugs * Fix build and entrypoint logic * Catch error if appKey changes * Add newToken logic * Fix lint (ignore any errors) * Add build for PRs * pnpm reformat-files result * Fix some tests * Fix reset password not working (test failing) * Restore csrfTokens (disabled by accident) * Fix pnpm start command with .env * Disable failing tests on the transfer endpoint (TODO) * Add tests to PR build * Fix build * Remove unnecessary assertStatus * Add typecheck * hash password on UserFactory (fix build) * Add JWT_USE_PEM true by default (increase security) * fix name of github action --------- Co-authored-by: Vijay A <vraravam@users.noreply.github.com> Co-authored-by: Balaji Vijayakumar <kuttibalaji.v6@gmail.com> Co-authored-by: MCMXC <16797721+mcmxcdev@users.noreply.github.com> Co-authored-by: André Oliveira <oliveira.andrerodrigues95@gmail.com>
Diffstat (limited to 'app/Controllers/Http/RecipeController.js')
-rw-r--r--app/Controllers/Http/RecipeController.js226
1 files changed, 0 insertions, 226 deletions
diff --git a/app/Controllers/Http/RecipeController.js b/app/Controllers/Http/RecipeController.js
deleted file mode 100644
index cbdddaf..0000000
--- a/app/Controllers/Http/RecipeController.js
+++ /dev/null
@@ -1,226 +0,0 @@
1const Recipe = use('App/Models/Recipe');
2const Helpers = use('Helpers');
3const Drive = use('Drive');
4const {
5 validateAll,
6} = use('Validator');
7const Env = use('Env');
8
9const targz = require('targz');
10const path = require('path');
11const fs = require('fs-extra');
12const semver = require('semver');
13
14const compress = (src, dest) => new Promise((resolve, reject) => {
15 targz.compress({
16 src,
17 dest,
18 }, (err) => {
19 if (err) {
20 reject(err);
21 } else {
22 resolve(dest);
23 }
24 });
25});
26
27class RecipeController {
28 // List official and custom recipes
29 async list({
30 response,
31 }) {
32 const officialRecipes = fs.readJsonSync(path.join(Helpers.appRoot(), 'recipes', 'all.json'));
33 const customRecipesArray = (await Recipe.all()).rows;
34 const customRecipes = customRecipesArray.map((recipe) => ({
35 id: recipe.recipeId,
36 name: recipe.name,
37 ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data,
38 }));
39
40 const recipes = [
41 ...officialRecipes,
42 ...customRecipes,
43 ];
44
45 return response.send(recipes);
46 }
47
48 // Create a new recipe using the new.html page
49 async create({
50 request,
51 response,
52 }) {
53 // Check if recipe creation is enabled
54 if (Env.get('IS_CREATION_ENABLED') == 'false') { // eslint-disable-line eqeqeq
55 return response.send('This server doesn\'t allow the creation of new recipes.');
56 }
57
58 // Validate user input
59 const validation = await validateAll(request.all(), {
60 name: 'required|string',
61 id: 'required|unique:recipes,recipeId',
62 author: 'required|accepted',
63 svg: 'required|url',
64 });
65 if (validation.fails()) {
66 return response.status(401).send({
67 message: 'Invalid POST arguments',
68 messages: validation.messages(),
69 status: 401,
70 });
71 }
72
73 const data = request.all();
74
75 if (!data.id) {
76 return response.send('Please provide an ID');
77 }
78
79 // Check for invalid characters
80 if (/\.{1,}/.test(data.id) || /\/{1,}/.test(data.id)) {
81 return response.send('Invalid recipe name. Your recipe name may not contain "." or "/"');
82 }
83
84 // Clear temporary recipe folder
85 await fs.emptyDir(Helpers.tmpPath('recipe'));
86
87 // Move uploaded files to temporary path
88 const files = request.file('files');
89 await files.moveAll(Helpers.tmpPath('recipe'));
90
91 // Compress files to .tar.gz file
92 const source = Helpers.tmpPath('recipe');
93 const destination = path.join(Helpers.appRoot(), `/recipes/archives/${data.id}.tar.gz`);
94
95 compress(
96 source,
97 destination,
98 );
99
100 // Create recipe in db
101 await Recipe.create({
102 name: data.name,
103 recipeId: data.id,
104 data: JSON.stringify({
105 author: data.author,
106 featured: false,
107 version: '1.0.0',
108 icons: {
109 svg: data.svg,
110 },
111 }),
112 });
113
114 return response.send('Created new recipe');
115 }
116
117 // Search official and custom recipes
118 async search({
119 request,
120 response,
121 }) {
122 // Validate user input
123 const validation = await validateAll(request.all(), {
124 needle: 'required',
125 });
126 if (validation.fails()) {
127 return response.status(401).send({
128 message: 'Please provide a needle',
129 messages: validation.messages(),
130 status: 401,
131 });
132 }
133
134 const needle = request.input('needle');
135
136 // Get results
137 let results;
138
139 if (needle === 'ferdium:custom') {
140 const dbResults = (await Recipe.all()).toJSON();
141 results = dbResults.map((recipe) => ({
142 id: recipe.recipeId,
143 name: recipe.name,
144 ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data,
145 }));
146 } else {
147 const localResultsArray = (await Recipe.query().where('name', 'LIKE', `%${needle}%`).fetch()).toJSON();
148 results = localResultsArray.map((recipe) => ({
149 id: recipe.recipeId,
150 name: recipe.name,
151 ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data,
152 }));
153 }
154
155 return response.send(results);
156 }
157
158 popularRecipes({
159 response,
160 }) {
161 return response.send(
162 fs
163 .readJsonSync(path.join(
164 Helpers.appRoot(), 'recipes', 'all.json',
165 ))
166 .filter((recipe) => recipe.featured),
167 );
168 }
169
170 update({ request, response }) {
171 const updates = [];
172 const recipes = request.all();
173 const allJson = fs.readJsonSync(path.join(
174 Helpers.appRoot(), 'recipes', 'all.json',
175 ));
176
177 for (const recipe of Object.keys(recipes)) {
178 const version = recipes[recipe];
179
180 // Find recipe in local recipe repository
181 const localRecipe = allJson.find(r => r.id === recipe);
182 if (localRecipe && semver.lt(version, localRecipe.version)) {
183 updates.push(recipe);
184 }
185 }
186
187 return response.send(updates);
188 }
189
190 // Download a recipe
191 async download({
192 response,
193 params,
194 }) {
195 // Validate user input
196 const validation = await validateAll(params, {
197 recipe: 'required|accepted',
198 });
199 if (validation.fails()) {
200 return response.status(401).send({
201 message: 'Please provide a recipe ID',
202 messages: validation.messages(),
203 status: 401,
204 });
205 }
206
207 const service = params.recipe;
208
209 // Check for invalid characters
210 if (/\.{1,}/.test(service) || /\/{1,}/.test(service)) {
211 return response.send('Invalid recipe name');
212 }
213
214 // Check if recipe exists in recipes folder
215 if (await Drive.exists(`${service}.tar.gz`)) {
216 return response.type('.tar.gz').send(await Drive.get(`${service}.tar.gz`));
217 }
218
219 return response.status(400).send({
220 message: 'Recipe not found',
221 code: 'recipe-not-found',
222 });
223 }
224}
225
226module.exports = RecipeController;