summaryrefslogtreecommitdiff
path: root/packages/backend/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src/server')
-rw-r--r--packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts17
-rw-r--r--packages/backend/src/server/web/UrlPreviewService.ts55
2 files changed, 60 insertions, 12 deletions
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
index 0dbfaae054..b8200c09aa 100644
--- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
@@ -69,6 +69,11 @@ export const meta = {
nullable: false, optional: false,
ref: 'UserDetailedNotMe',
},
+ targetInstance: {
+ type: 'object',
+ nullable: true, optional: false,
+ ref: 'FederationInstance',
+ },
assignee: {
type: 'object',
nullable: true, optional: false,
@@ -115,7 +120,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
- const query = this.queryService.makePaginationQuery(this.abuseUserReportsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId);
+ const query = this.queryService.makePaginationQuery(this.abuseUserReportsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId)
+ .leftJoinAndSelect('report.targetUser', 'targetUser')
+ .leftJoinAndSelect('targetUser.userProfile', 'targetUserProfile')
+ .leftJoinAndSelect('report.targetUserInstance', 'targetUserInstance')
+ .leftJoinAndSelect('report.reporter', 'reporter')
+ .leftJoinAndSelect('reporter.userProfile', 'reporterProfile')
+ .leftJoinAndSelect('report.assignee', 'assignee')
+ .leftJoinAndSelect('assignee.userProfile', 'assigneeProfile')
+ ;
switch (ps.state) {
case 'resolved': query.andWhere('report.resolved = TRUE'); break;
@@ -134,7 +147,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const reports = await query.limit(ps.limit).getMany();
- return await this.abuseUserReportEntityService.packMany(reports);
+ return await this.abuseUserReportEntityService.packMany(reports, me);
});
}
}
diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts
index 2a300782c6..78b2204fbb 100644
--- a/packages/backend/src/server/web/UrlPreviewService.ts
+++ b/packages/backend/src/server/web/UrlPreviewService.ts
@@ -123,26 +123,45 @@ export class UrlPreviewService {
request: FastifyRequest<PreviewRoute>,
reply: FastifyReply,
): Promise<void> {
+ if (!this.meta.urlPreviewEnabled) {
+ return reply.code(403).send({
+ error: {
+ message: 'URL preview is disabled',
+ code: 'URL_PREVIEW_DISABLED',
+ id: '58b36e13-d2f5-0323-b0c6-76aa9dabefb8',
+ },
+ });
+ }
+
const url = request.query.url;
if (typeof url !== 'string' || !URL.canParse(url)) {
reply.code(400);
return;
}
+ // Enforce HTTP(S) for input URLs
+ const urlScheme = this.utilityService.getUrlScheme(url);
+ if (urlScheme !== 'http:' && urlScheme !== 'https:') {
+ reply.code(400);
+ return;
+ }
+
const lang = request.query.lang;
if (Array.isArray(lang)) {
reply.code(400);
return;
}
- if (!this.meta.urlPreviewEnabled) {
- return reply.code(403).send({
- error: {
- message: 'URL preview is disabled',
- code: 'URL_PREVIEW_DISABLED',
- id: '58b36e13-d2f5-0323-b0c6-76aa9dabefb8',
- },
- });
+ // Strip out hash (anchor)
+ const urlObj = new URL(url);
+ if (urlObj.hash) {
+ urlObj.hash = '';
+ const params = new URLSearchParams({ url: urlObj.href });
+ if (lang) params.set('lang', lang);
+ const newUrl = `/url?${params.toString()}`;
+
+ reply.redirect(newUrl, 301);
+ return;
}
// Check rate limit
@@ -151,7 +170,7 @@ export class UrlPreviewService {
return;
}
- if (this.utilityService.isBlockedHost(this.meta.blockedHosts, new URL(url).host)) {
+ if (this.utilityService.isBlockedHost(this.meta.blockedHosts, urlObj.host)) {
return reply.code(403).send({
error: {
message: 'URL is blocked',
@@ -166,7 +185,7 @@ export class UrlPreviewService {
return;
}
- const cacheKey = `${url}@${lang}@${cacheFormatVersion}`;
+ const cacheKey = getCacheKey(url, lang);
if (await this.sendCachedPreview(cacheKey, reply, fetch)) {
return;
}
@@ -217,6 +236,18 @@ export class UrlPreviewService {
// Await this to avoid hammering redis when a bunch of URLs are fetched at once
await this.previewCache.set(cacheKey, summary);
+ // Also cache the response URL in case of redirects
+ if (summary.url !== url) {
+ const responseCacheKey = getCacheKey(summary.url, lang);
+ await this.previewCache.set(responseCacheKey, summary);
+ }
+
+ // Also cache the ActivityPub URL, if different from the others
+ if (summary.activityPub && summary.activityPub !== summary.url) {
+ const apCacheKey = getCacheKey(summary.activityPub, lang);
+ await this.previewCache.set(apCacheKey, summary);
+ }
+
// Cache 1 day (matching redis), but only once we finalize the result
if (!summary.activityPub || summary.haveNoteLocally) {
reply.header('Cache-Control', 'public, max-age=86400');
@@ -533,3 +564,7 @@ export class UrlPreviewService {
return true;
}
}
+
+function getCacheKey(url: string, lang = 'none') {
+ return `${url}@${lang}@${cacheFormatVersion}`;
+}