aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar vantezzen <properly@protonmail.com>2019-08-23 14:04:22 +0200
committerLibravatar vantezzen <properly@protonmail.com>2019-08-23 14:04:22 +0200
commit5970b8e5bbf993c88c1f901708a7c5075a916770 (patch)
tree11636435cba3414a930b4a81f9bf7ca8d4de31e1
parentFix user login (diff)
downloadferdium-server-5970b8e5bbf993c88c1f901708a7c5075a916770.tar.gz
ferdium-server-5970b8e5bbf993c88c1f901708a7c5075a916770.tar.zst
ferdium-server-5970b8e5bbf993c88c1f901708a7c5075a916770.zip
Add support for workspaces
-rw-r--r--app/Controllers/Http/RecipeController.js27
-rw-r--r--app/Controllers/Http/ServiceController.js2
-rw-r--r--app/Controllers/Http/WorkspaceController.js116
-rw-r--r--app/Models/Recipe.js9
-rw-r--r--app/Models/User.js4
-rw-r--r--app/Models/Workspace.js12
-rw-r--r--database/migrations/1566554231482_recipe_schema.js22
-rw-r--r--database/migrations/1566554359294_workspace_schema.js25
-rw-r--r--reqs.txt5
-rw-r--r--start/routes.js18
10 files changed, 234 insertions, 6 deletions
diff --git a/app/Controllers/Http/RecipeController.js b/app/Controllers/Http/RecipeController.js
new file mode 100644
index 0000000..0b9d488
--- /dev/null
+++ b/app/Controllers/Http/RecipeController.js
@@ -0,0 +1,27 @@
1'use strict'
2
3const Recipe = use('App/Models/Recipe');
4const fetch = require('node-fetch');
5
6class RecipeController {
7 async list({
8 response
9 }) {
10 const officialRecipes = JSON.parse(await (await fetch('https://api.franzinfra.com/v1/recipes')).text());
11 const customRecipesArray = (await Recipe.all()).rows;
12 const customRecipes = customRecipesArray.map(recipe => ({
13 "id": recipe.recipeId,
14 "name": recipe.name,
15 ...JSON.parse(recipe.data)
16 }))
17
18 const recipes = [
19 ...officialRecipes,
20 ...customRecipes,
21 ]
22
23 return response.send(recipes)
24 }
25}
26
27module.exports = RecipeController
diff --git a/app/Controllers/Http/ServiceController.js b/app/Controllers/Http/ServiceController.js
index 0fcbec0..4c908ac 100644
--- a/app/Controllers/Http/ServiceController.js
+++ b/app/Controllers/Http/ServiceController.js
@@ -23,7 +23,7 @@ class ServiceController {
23 let serviceId; 23 let serviceId;
24 do { 24 do {
25 serviceId = uuid(); 25 serviceId = uuid();
26 } while((await Service.all()).rows.length > 0) 26 } while((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0)
27 27
28 const service = await Service.create({ 28 const service = await Service.create({
29 userId: auth.user.id, 29 userId: auth.user.id,
diff --git a/app/Controllers/Http/WorkspaceController.js b/app/Controllers/Http/WorkspaceController.js
new file mode 100644
index 0000000..55a0c75
--- /dev/null
+++ b/app/Controllers/Http/WorkspaceController.js
@@ -0,0 +1,116 @@
1'use strict'
2
3const Workspace = use('App/Models/Workspace');
4const uuid = require('uuid/v4');
5
6class WorkspaceController {
7 // Create a new workspace for user
8 async create({
9 request,
10 response,
11 auth
12 }) {
13 try {
14 await auth.getUser()
15 } catch (error) {
16 return response.send('Missing or invalid api token')
17 }
18
19 const data = request.all();
20
21 // Get new, unused uuid
22 let workspaceId;
23 do {
24 workspaceId = uuid();
25 } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0)
26
27 const order = (await auth.user.workspaces().fetch()).rows.length;
28
29 await Workspace.create({
30 userId: auth.user.id,
31 workspaceId,
32 name: data.name,
33 order,
34 services: JSON.stringify([]),
35 data: JSON.stringify(data)
36 });
37
38 return response.send({
39 userId: auth.user.id,
40 name: data.name,
41 id: workspaceId,
42 order,
43 workspaces: [],
44 })
45 }
46
47 async edit({
48 request,
49 response,
50 auth,
51 params
52 }) {
53 try {
54 await auth.getUser()
55 } catch (error) {
56 return response.send('Missing or invalid api token')
57 }
58
59 const data = request.all();
60 const {
61 id
62 } = params;
63
64 // Update data in database
65 await (Workspace.query()
66 .where('workspaceId', id)
67 .where('userId', auth.user.id)).update({
68 name: data.name,
69 services: JSON.stringify(data.services)
70 });
71
72 // Get updated row
73 const workspace = (await Workspace.query()
74 .where('workspaceId', id)
75 .where('userId', auth.user.id).fetch()).rows[0];
76
77 return response.send({
78 "id": workspace.workspaceId,
79 "name": data.name,
80 "order": workspace.order,
81 "services": data.services,
82 "userId": auth.user.id
83 })
84 }
85
86 // List all workspaces a user has created
87 async list({
88 request,
89 response,
90 auth
91 }) {
92 try {
93 await auth.getUser()
94 } catch (error) {
95 return response.send('Missing or invalid api token')
96 }
97
98 const workspaces = (await auth.user.workspaces().fetch()).rows;
99 // Convert to array with all data Franz wants
100 let workspacesArray = [];
101 if(workspaces) {
102 workspacesArray = workspaces.map(workspace => ({
103 "id": workspace.workspaceId,
104 "name": workspace.name,
105 "order": workspace.order,
106 "services": JSON.parse(workspace.services),
107 "userId": auth.user.id
108 }))
109 }
110
111
112 return response.send(workspacesArray)
113 }
114}
115
116module.exports = WorkspaceController
diff --git a/app/Models/Recipe.js b/app/Models/Recipe.js
new file mode 100644
index 0000000..9e3619c
--- /dev/null
+++ b/app/Models/Recipe.js
@@ -0,0 +1,9 @@
1'use strict'
2
3/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
4const Model = use('Model')
5
6class Recipe extends Model {
7}
8
9module.exports = Recipe
diff --git a/app/Models/User.js b/app/Models/User.js
index 0bb1547..c9a680a 100644
--- a/app/Models/User.js
+++ b/app/Models/User.js
@@ -38,6 +38,10 @@ class User extends Model {
38 services () { 38 services () {
39 return this.hasMany('App/Models/Service', 'id', 'userId') 39 return this.hasMany('App/Models/Service', 'id', 'userId')
40 } 40 }
41
42 workspaces () {
43 return this.hasMany('App/Models/Workspace', 'id', 'userId')
44 }
41} 45}
42 46
43module.exports = User 47module.exports = User
diff --git a/app/Models/Workspace.js b/app/Models/Workspace.js
new file mode 100644
index 0000000..f78a3f9
--- /dev/null
+++ b/app/Models/Workspace.js
@@ -0,0 +1,12 @@
1'use strict'
2
3/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
4const Model = use('Model')
5
6class Workspace extends Model {
7 user() {
8 return this.belongsTo('App/Models/User', 'userId', 'id')
9 }
10}
11
12module.exports = Workspace
diff --git a/database/migrations/1566554231482_recipe_schema.js b/database/migrations/1566554231482_recipe_schema.js
new file mode 100644
index 0000000..7d8f5a8
--- /dev/null
+++ b/database/migrations/1566554231482_recipe_schema.js
@@ -0,0 +1,22 @@
1'use strict'
2
3/** @type {import('@adonisjs/lucid/src/Schema')} */
4const Schema = use('Schema')
5
6class RecipeSchema extends Schema {
7 up () {
8 this.create('recipes', (table) => {
9 table.increments()
10 table.string('name', 80).notNullable()
11 table.string('recipeId', 254).notNullable().unique()
12 table.json('data')
13 table.timestamps()
14 })
15 }
16
17 down () {
18 this.drop('recipes')
19 }
20}
21
22module.exports = RecipeSchema
diff --git a/database/migrations/1566554359294_workspace_schema.js b/database/migrations/1566554359294_workspace_schema.js
new file mode 100644
index 0000000..84e0bb9
--- /dev/null
+++ b/database/migrations/1566554359294_workspace_schema.js
@@ -0,0 +1,25 @@
1'use strict'
2
3/** @type {import('@adonisjs/lucid/src/Schema')} */
4const Schema = use('Schema')
5
6class WorkspaceSchema extends Schema {
7 up () {
8 this.create('workspaces', (table) => {
9 table.increments()
10 table.string('workspaceId', 80).notNullable().unique()
11 table.string('userId', 80).notNullable()
12 table.string('name', 80).notNullable()
13 table.integer('order')
14 table.json('services')
15 table.json('data')
16 table.timestamps()
17 })
18 }
19
20 down () {
21 this.drop('workspaces')
22 }
23}
24
25module.exports = WorkspaceSchema
diff --git a/reqs.txt b/reqs.txt
new file mode 100644
index 0000000..3940f02
--- /dev/null
+++ b/reqs.txt
@@ -0,0 +1,5 @@
1GET /recipes (with auth) // All recipes
2[{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"discord","name":"Discord","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/discord/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/discord/src/icon.svg"}},{"author":"Brian Gilbert <brian@briangilbert.net>","featured":false,"id":"gitter","name":"Gitter","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/gitter/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/gitter/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"gmail","name":"Gmail","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/gmail/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/gmail/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"hangouts","name":"Hangouts","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/hangouts/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/hangouts/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"hipchat","name":"HipChat","version":"1.0.1","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/hipchat/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/hipchat/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"gmailinbox","name":"Inbox by Gmail","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/gmailinbox/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/gmailinbox/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"mattermost","name":"Mattermost","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/mattermost/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/mattermost/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"messenger","name":"Messenger","version":"1.0.6","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/messenger/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/messenger/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"mysms","name":"MySMS","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/mysms/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/mysms/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"rocketchat","name":"Rocket.Chat","version":"1.0.1","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/rocketchat/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/rocketchat/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"skype","name":"Skype","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/skype/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/skype/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"slack","name":"Slack","version":"1.0.4","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/slack/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/slack/src/icon.svg"}},{"author":"Brian Gilbert <brian@briangilbert.net>","featured":false,"id":"tawk","name":"Tawk.to","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/tawk/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/tawk/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"telegram","name":"Telegram","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/telegram/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/telegram/src/icon.svg"}},{"author":"Franz <recipe@meetfranz.com>","featured":false,"id":"toggl","name":"toggl","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/toggl/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/toggl/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"tweetdeck","name":"Tweetdeck","version":"1.0.1","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/tweetdeck/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/tweetdeck/src/icon.svg"}},{"author":"Stuart Clark <stuart@realityloop.com>","featured":false,"id":"twist","name":"twist","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/twist/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/twist/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"whatsapp","name":"WhatsApp","version":"1.0.1","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/whatsapp/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/whatsapp/src/icon.svg"}}]
3
4GET /recipes/search?needle=query // Search recipes
5[{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"gmail","name":"Gmail","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/gmail/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/gmail/src/icon.svg"}},{"author":"Stefan Malzner <stefan@adlk.io>","featured":false,"id":"gmailinbox","name":"Inbox by Gmail","version":"1.0.0","icons":{"png":"https://cdn.franzinfra.com/recipes/dist/gmailinbox/src/icon.png","svg":"https://cdn.franzinfra.com/recipes/dist/gmailinbox/src/icon.svg"}}] \ No newline at end of file
diff --git a/start/routes.js b/start/routes.js
index ef688c8..da12a1b 100644
--- a/start/routes.js
+++ b/start/routes.js
@@ -29,19 +29,27 @@ Route.group(() => {
29 // User info 29 // User info
30 Route.get('me', 'UserController.me').middleware('auth') 30 Route.get('me', 'UserController.me').middleware('auth')
31 31
32 // Service/recipe info 32 // Service info
33 Route.post('service', 'ServiceController.create').middleware('auth') 33 Route.post('service', 'ServiceController.create').middleware('auth')
34 Route.get('me/services', 'ServiceController.list').middleware('auth') 34 Route.get('me/services', 'ServiceController.list').middleware('auth')
35 Route.get('recipe', 'ServiceController.list').middleware('auth')
36
37 // Recipe store
38 Route.get('recipes', 'RecipeController.list')
35 Route.get('recipes/download/:recipe', 'ServiceController.download') 39 Route.get('recipes/download/:recipe', 'ServiceController.download')
40 Route.get('recipes/popular', 'StaticController.popularRecipes')
41 Route.get('recipes/update', 'StaticController.emptyArray')
42
43 // Workspaces
44 Route.put('workspace/:id', 'WorkspaceController.edit').middleware('auth')
45 Route.post('workspace', 'WorkspaceController.create').middleware('auth')
46 Route.get('workspace', 'WorkspaceController.list').middleware('auth')
36 47
37 // Static responses 48 // Static responses
38 Route.get('features', 'StaticController.features'); 49 Route.get('features', 'StaticController.features')
39 Route.get('services', 'StaticController.emptyArray') 50 Route.get('services', 'StaticController.emptyArray')
40 Route.get('workspace', 'StaticController.emptyArray')
41 Route.get('news', 'StaticController.emptyArray') 51 Route.get('news', 'StaticController.emptyArray')
42 Route.get('payment/plans', 'StaticController.plans') 52 Route.get('payment/plans', 'StaticController.plans')
43 Route.get('recipes/popular', 'StaticController.popularRecipes')
44 Route.get('recipes/update', 'StaticController.emptyArray')
45 // Route.get('announcements/:version', 'StaticController.announcement') 53 // Route.get('announcements/:version', 'StaticController.announcement')
46}).prefix('v1') 54}).prefix('v1')
47 55