Skip to content

Commit 5d214c7

Browse files
committed
feat: add /share/file-by-username endpoint
1 parent 1859668 commit 5d214c7

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed

packages/backend/src/api/APIError.js

+6
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,12 @@ module.exports = class APIError {
388388
message: ({ identifier }) => `Entity not found: ${quot(identifier)}`,
389389
},
390390

391+
// Share
392+
'user_does_not_exist': {
393+
status: 422,
394+
message: ({ username }) => `The user ${quot(username)} does not exist.`
395+
},
396+
391397
// Chat
392398
// TODO: specifying these errors here might be a violation
393399
// of separation of concerns. Services could register their

packages/backend/src/routers/share.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const express = require('express');
2+
const { Endpoint } = require('../util/expressutil');
3+
4+
const validator = require('validator');
5+
const APIError = require('../api/APIError');
6+
const { get_user } = require('../helpers');
7+
const { Context } = require('../util/context');
8+
const auth2 = require('../middleware/auth2');
9+
const config = require('../config');
10+
11+
const { PermissionUtil } = require('../services/auth/PermissionService');
12+
13+
const uuidv4 = require('uuid').v4;
14+
15+
const router = express.Router();
16+
router.use(auth2);
17+
18+
Endpoint({
19+
route: '/file-by-username',
20+
methods: ['POST'],
21+
handler: async (req, res) => {
22+
const svc_token = req.services.get('token');
23+
const svc_email = req.services.get('email');
24+
const svc_permission = req.services.get('permission');
25+
26+
console.log('which actor exists?',
27+
req.actor,
28+
Context.get('actor'))
29+
const actor = Context.get('actor');
30+
31+
const username = req.body.username;
32+
if ( ! (typeof username === 'string') ) {
33+
throw APIError.create('field_invalid', null, {
34+
key: 'username',
35+
expected: 'string',
36+
got: typeof username,
37+
});
38+
}
39+
40+
let path = req.body.path;
41+
if ( ! (typeof path === 'string') ) {
42+
throw APIError.create('field_invalid', null, {
43+
key: 'path',
44+
expected: 'string',
45+
got: typeof path,
46+
});
47+
}
48+
49+
const access_level = req.body.access_level || 'write';
50+
if ( ! ['read','write'].includes(access_level) ) {
51+
throw APIError.create('field_invalid', null, {
52+
key: 'access_level',
53+
expected: '"read" or "write"',
54+
});
55+
}
56+
57+
const recipient = await get_user({ username });
58+
if ( ! recipient ) {
59+
throw APIError.create('user_does_not_exist', null, {
60+
username
61+
});
62+
}
63+
64+
await svc_permission.grant_user_user_permission(
65+
actor, username,
66+
PermissionUtil.join('fs', path, access_level),
67+
{}, {},
68+
);
69+
70+
if ( path.startsWith('/') ) path = path.slice(1);
71+
const link = `${config.origin}/show/${path}`;
72+
73+
const email_values = {
74+
link,
75+
susername: req.user.username,
76+
...(recipient ? {
77+
rusername: recipient.username,
78+
} : {}),
79+
};
80+
81+
const email_tmpl = recipient ?
82+
'share_existing_user' : 'share_new_user';
83+
84+
await svc_email.send_email({ email: recipient.email }, email_tmpl, email_values);
85+
86+
res.send({});
87+
},
88+
}).attach(router);
89+
90+
module.exports = router;

packages/backend/src/services/EmailService.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,17 @@ If this was not you, please contact [email protected] immediately.
130130
<p>Sincerely,</p>
131131
<p>Puter</p>
132132
`
133-
}
133+
},
134+
// TODO: revise email contents
135+
'share_existing_user': {
136+
subject: 'Puter share from {{susername}}',
137+
html: `
138+
<p>Hi there {{rusername}},</p>
139+
<p>{{link}}</p>
140+
<p>Sincerely,</p>
141+
<p>Puter</p>
142+
`
143+
},
134144
}
135145

136146
class Emailservice extends BaseService {

packages/backend/src/services/PuterAPIService.js

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class PuterAPIService extends BaseService {
7676
app.use(require('../routers/healthcheck'))
7777
app.use(require('../routers/test'))
7878
app.use(require('../routers/update-taskbar-items'))
79+
app.use('/share', require('../routers/share'));
7980
require('../routers/whoami')(app);
8081

8182
}

packages/backend/src/services/auth/PermissionService.js

+20
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,33 @@ class PermissionUtil {
158158
}
159159
return unescaped_str;
160160
}
161+
162+
static escape_permission_component (component) {
163+
let escaped_str = '';
164+
for ( let i = 0 ; i < component.length ; i++ ) {
165+
const c = component[i];
166+
if ( c === ':' ) {
167+
escaped_str += '\\C';
168+
continue;
169+
}
170+
escaped_str += c;
171+
}
172+
return escaped_str;
173+
}
161174

162175
static split (permission) {
163176
return permission
164177
.split(':')
165178
.map(PermissionUtil.unescape_permission_component)
166179
;
167180
}
181+
182+
static join (...components) {
183+
return components
184+
.map(PermissionUtil.escape_permission_component)
185+
.join(':')
186+
;
187+
}
168188
}
169189

170190
class PermissionService extends BaseService {

0 commit comments

Comments
 (0)