Skip to content

Commit 742f625

Browse files
committed
feat: add /sharelink/request endpoint
1 parent b0e067e commit 742f625

File tree

4 files changed

+95
-4
lines changed

4 files changed

+95
-4
lines changed

doc/devmeta/track-comments.md

+12
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,15 @@ Comments beginning with `// track:`. See
3434
or there's a common behavior that should happen
3535
in between. The Sequence class is good for this so
3636
it might be a worthy migration.
37+
- `track: opposite condition of sibling`
38+
A sibling class, function, method, or other construct of
39+
source code has a boolean expression which always evaluates
40+
to the opposite of the one below this track comment.
41+
- `track: null check before processing`
42+
An object could be undefined or null, additional processing
43+
occurs after a null check, and the unprocessed object is not
44+
relevant to the rest of the code. If the code for obtaining
45+
the object and processing it is moved to a function outside,
46+
then the null check should result in a early return of null;
47+
this code with the track comment may have additional logic
48+
for the null/undefined case.

packages/backend/src/api/APIError.js

+5
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,11 @@ module.exports = class APIError {
462462
status: 422,
463463
message: 'Email must be confirmed to apply a share.',
464464
},
465+
'no_need_to_request': {
466+
status: 422,
467+
message: 'This share is already valid for this user; ' +
468+
'POST to /apply for access.'
469+
},
465470
'can_not_apply_to_this_user': {
466471
status: 422,
467472
message: 'This share can not be applied to this user.',

packages/backend/src/services/ShareService.js

+73-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const { whatis } = require("../util/langutil");
66
const { Actor, UserActorType } = require("./auth/Actor");
77
const BaseService = require("./BaseService");
88
const { DB_WRITE } = require("./database/consts");
9+
const { UsernameNotifSelector } = require("./NotificationService");
910

1011
class ShareService extends BaseService {
1112
static MODULES = {
@@ -85,16 +86,16 @@ class ShareService extends BaseService {
8586
uid: share_uid,
8687
});
8788

89+
if ( ! share ) {
90+
throw APIError.create('share_expired');
91+
}
92+
8893
share.data = this.db.case({
8994
mysql: () => share.data,
9095
otherwise: () =>
9196
JSON.parse(share.data ?? '{}'),
9297
})();
9398

94-
if ( ! share ) {
95-
throw APIError.create('share_expired');
96-
}
97-
9899
const actor = Actor.adapt(req.actor ?? req.user);
99100
if ( ! actor ) {
100101
// this shouldn't happen; auth should catch it
@@ -137,6 +138,74 @@ class ShareService extends BaseService {
137138
});
138139
}
139140
}).attach(router);
141+
142+
Endpoint({
143+
route: '/request',
144+
methods: ['POST'],
145+
mw: [configurable_auth()],
146+
handler: async (req, res) => {
147+
const share_uid = req.body.uid;
148+
149+
const share = await svc_share.get_share({
150+
uid: share_uid,
151+
});
152+
153+
// track: null check before processing
154+
if ( ! share ) {
155+
throw APIError.create('share_expired');
156+
}
157+
158+
share.data = this.db.case({
159+
mysql: () => share.data,
160+
otherwise: () =>
161+
JSON.parse(share.data ?? '{}'),
162+
})();
163+
164+
const actor = Actor.adapt(req.actor ?? req.user);
165+
if ( ! actor ) {
166+
// this shouldn't happen; auth should catch it
167+
throw new Error('actor missing');
168+
}
169+
170+
// track: opposite condition of sibling
171+
// :: sibling: /apply endpoint
172+
if (
173+
actor.type.user.email_confirmed &&
174+
actor.type.user.email === share.recipient_email
175+
) {
176+
throw APIError.create('no_need_to_request');
177+
}
178+
179+
const issuer_user = await get_user({
180+
id: share.issuer_user_id,
181+
});
182+
183+
if ( ! issuer_user ) {
184+
throw APIError.create('share_expired');
185+
}
186+
187+
const svc_notification = this.services.get('notification');
188+
svc_notification.notify(
189+
UsernameNotifSelector(issuer_user.username),
190+
{
191+
source: 'sharing',
192+
title: `User ${actor.type.user.username} is ` +
193+
`trying to open a share you sent to ` +
194+
share.recipient_email,
195+
template: 'user-requesting-share',
196+
fields: {
197+
username: actor.type.user.username,
198+
intended_recipient: share.recipient_email,
199+
permissions: share.data.permissions,
200+
},
201+
}
202+
);
203+
res.json({
204+
$: 'api:status-report',
205+
status: 'success',
206+
});
207+
}
208+
}).attach(router);
140209
}
141210

142211
async get_share ({ uid }) {

packages/backend/src/services/drivers/DriverService.js

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class DriverService extends BaseService {
3434
this.interfaces = require('./interfaces');
3535
this.interface_to_implementation = {};
3636
}
37+
38+
_init () {
39+
const svc_registry = this.services.get('registry');
40+
svc_registry.register_collection('');
41+
}
3742

3843
register_driver (interface_name, implementation) {
3944
this.interface_to_implementation[interface_name] = implementation;

0 commit comments

Comments
 (0)