aboutsummaryrefslogtreecommitdiffstats
path: root/src/internal-server/start/routes.js
blob: 2dbd0713e09eedc7aebcdfc330c196ae25d28723 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
|--------------------------------------------------------------------------
| Routes
|--------------------------------------------------------------------------
|
*/

const { timingSafeEqual } = require('node:crypto');

/** @type {typeof import('@adonisjs/framework/src/Route/Manager')} */
const Route = use('Route');

const { API_VERSION } = require('../../environment-remote');
// Run latest database migration
const migrate = require('./migrate');

migrate();

async function validateToken(clientToken, response, next) {
  const serverToken = process.env.FERDIUM_LOCAL_TOKEN;
  const valid =
    serverToken &&
    clientToken &&
    timingSafeEqual(
      Buffer.from(clientToken, 'utf8'),
      Buffer.from(serverToken, 'utf8'),
    );
  if (valid) {
    await next();
    return true;
  }
  return response.forbidden();
}

const OnlyAllowFerdium = async ({ request, response }, next) => {
  const version = request.header('X-Franz-Version');
  if (!version) {
    return response.forbidden();
  }

  const clientToken = request.header('X-Ferdium-Local-Token');
  return validateToken(clientToken, response, next);
};

const RequireTokenInQS = async ({ request, response }, next) => {
  const clientToken = request.get().token;
  return validateToken(clientToken, response, next);
};

const FERDIUM_LOCAL_TOKEN_COOKIE = 'ferdium-local-token';

const RequireAuthenticatedBrowser = async ({ request, response }, next) => {
  const clientToken = request.cookie(FERDIUM_LOCAL_TOKEN_COOKIE);
  return validateToken(clientToken, response, next);
};

// Health: Returning if all systems function correctly
Route.get('health', ({ response }) =>
  response.send({
    api: 'success',
    db: 'success',
  }),
).middleware(OnlyAllowFerdium);

// API is grouped under '/v1/' route
Route.group(() => {
  // User authentification
  Route.post('auth/signup', 'UserController.signup');
  Route.post('auth/login', 'UserController.login');

  // User info
  Route.get('me', 'UserController.me');
  Route.put('me', 'UserController.updateMe');

  // Service info
  Route.post('service', 'ServiceController.create');
  Route.put('service/reorder', 'ServiceController.reorder');
  Route.put('service/:id', 'ServiceController.edit');
  Route.delete('service/:id', 'ServiceController.delete');
  Route.get('me/services', 'ServiceController.list');

  // Recipe store
  Route.get('recipes', 'RecipeController.list');
  Route.get('recipes/search', 'RecipeController.search');
  Route.get('recipes/popular', 'RecipeController.popularRecipes');
  Route.get('recipes/download/:recipe', 'RecipeController.download');
  Route.post('recipes/update', 'RecipeController.update');

  // Workspaces
  Route.put('workspace/:id', 'WorkspaceController.edit');
  Route.delete('workspace/:id', 'WorkspaceController.delete');
  Route.post('workspace', 'WorkspaceController.create');
  Route.get('workspace', 'WorkspaceController.list');
})
  .prefix(API_VERSION)
  .middleware(OnlyAllowFerdium);

Route.group(() => {
  Route.get('icon/:id', 'ImageController.icon');
})
  .prefix(API_VERSION)
  .middleware(RequireTokenInQS);

Route.group(() => {
  // Franz account import
  Route.post('import', 'UserController.import');
  Route.get('import', ({ view }) => view.render('import'));

  // Account transfer
  Route.get('export', 'UserController.export');
  Route.post('transfer', 'UserController.importFerdium');
  Route.get('transfer', ({ view }) => view.render('transfer'));

  // Index
  Route.get('/', ({ view }) => view.render('index'));
}).middleware(RequireAuthenticatedBrowser);

Route.get('token/:token', ({ params: { token }, response }) => {
  if (validateToken(token)) {
    response.cookie(FERDIUM_LOCAL_TOKEN_COOKIE, token, {
      httpOnly: true,
      sameSite: true,
      path: '/',
    });
    return response.redirect('/');
  }
  return response.forbidden();
});