Fix timeline rendering

This commit is contained in:
Jesse Wierzbinski 2023-11-28 12:57:48 -10:00
parent 73cb7db6b3
commit 440e994576
No known key found for this signature in database
7 changed files with 359 additions and 20 deletions

View file

@ -0,0 +1,72 @@
import { errorResponse, jsonResponse } from "@response";
import {
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { client } from "~database/datasource";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/accounts/search",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
export default async (req: Request): Promise<Response> => {
// TODO: Add checks for disabled or not email verified accounts
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);
const {
following = false,
limit = 40,
offset,
q,
} = await parseRequest<{
q?: string;
limit?: number;
offset?: number;
resolve?: boolean;
following?: boolean;
}>(req);
if (limit < 1 || limit > 80) {
return errorResponse("Limit must be between 1 and 80", 400);
}
// TODO: Add WebFinger resolve
const accounts = await client.user.findMany({
where: {
displayName: {
contains: q,
},
username: {
contains: q,
},
relationshipSubjects: following
? {
some: {
ownerId: user.id,
following,
},
}
: undefined,
},
take: Number(limit),
skip: Number(offset || 0),
include: userRelations,
});
return jsonResponse(accounts.map(acct => userToAPI(acct)));
};

View file

@ -0,0 +1,79 @@
import { errorResponse, jsonResponse } from "@response";
import { getFromRequest, userRelations } from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource";
import type { MatchedRoute } from "bun";
import {
checkForBidirectionalRelationships,
relationshipToAPI,
} from "~database/entities/Relationship";
export const meta = applyConfig({
allowedMethods: ["POST"],
route: "/api/v1/follow_requests/:account_id/authorize",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);
const { account_id } = matchedRoute.params;
const account = await client.user.findUnique({
where: {
id: account_id,
},
include: userRelations,
});
if (!account) return errorResponse("Account not found", 404);
// Check if there is a relationship on both sides
await checkForBidirectionalRelationships(user, account);
// Authorize follow request
await client.relationship.updateMany({
where: {
subjectId: user.id,
ownerId: account.id,
requested: true,
},
data: {
requested: false,
following: true,
},
});
// Update followedBy for other user
await client.relationship.updateMany({
where: {
subjectId: account.id,
ownerId: user.id,
},
data: {
followedBy: true,
},
});
const relationship = await client.relationship.findFirst({
where: {
subjectId: account.id,
ownerId: user.id,
},
});
if (!relationship) return errorResponse("Relationship not found", 404);
return jsonResponse(relationshipToAPI(relationship));
};

View file

@ -0,0 +1,67 @@
import { errorResponse, jsonResponse } from "@response";
import { getFromRequest, userRelations } from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource";
import type { MatchedRoute } from "bun";
import {
checkForBidirectionalRelationships,
relationshipToAPI,
} from "~database/entities/Relationship";
export const meta = applyConfig({
allowedMethods: ["POST"],
route: "/api/v1/follow_requests/:account_id/reject",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);
const { account_id } = matchedRoute.params;
const account = await client.user.findUnique({
where: {
id: account_id,
},
include: userRelations,
});
if (!account) return errorResponse("Account not found", 404);
// Check if there is a relationship on both sides
await checkForBidirectionalRelationships(user, account);
// Reject follow request
await client.relationship.updateMany({
where: {
subjectId: user.id,
ownerId: account.id,
requested: true,
},
data: {
requested: false,
},
});
const relationship = await client.relationship.findFirst({
where: {
subjectId: account.id,
ownerId: user.id,
},
});
if (!relationship) return errorResponse("Relationship not found", 404);
return jsonResponse(relationshipToAPI(relationship));
};

View file

@ -0,0 +1,82 @@
import { errorResponse, jsonResponse } from "@response";
import {
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource";
import { parseRequest } from "@request";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/follow_requests",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
export default async (req: Request): Promise<Response> => {
const { user } = await getFromRequest(req);
const {
limit = 20,
max_id,
min_id,
since_id,
} = await parseRequest<{
max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
}>(req);
if (limit < 1 || limit > 40) {
return errorResponse("Limit must be between 1 and 40", 400);
}
if (!user) return errorResponse("Unauthorized", 401);
const objects = await client.user.findMany({
where: {
id: {
lt: max_id ?? undefined,
gte: since_id ?? undefined,
gt: min_id ?? undefined,
},
relationships: {
some: {
subjectId: user.id,
requested: true,
},
},
},
include: userRelations,
take: limit,
orderBy: {
id: "desc",
},
});
// Constuct HTTP Link header (next and prev)
const linkHeader = [];
if (objects.length > 0) {
const urlWithoutQuery = req.url.split("?")[0];
linkHeader.push(
`<${urlWithoutQuery}?max_id=${objects.at(-1)?.id}>; rel="next"`,
`<${urlWithoutQuery}?min_id=${objects[0].id}>; rel="prev"`
);
}
return jsonResponse(
objects.map(user => userToAPI(user)),
200,
{
Link: linkHeader.join(", "),
}
);
};

View file

@ -50,21 +50,33 @@ export default async (req: Request): Promise<Response> => {
gte: since_id ?? undefined,
gt: min_id ?? undefined,
},
author: {
OR: [
{
relationships: {
some: {
subjectId: user.id,
following: true,
OR: [
{
author: {
OR: [
{
relationshipSubjects: {
some: {
ownerId: user.id,
following: true,
},
},
},
{
id: user.id,
},
],
},
},
{
// Include posts where the user is mentioned in addition to posts by followed users
mentions: {
some: {
id: user.id,
},
},
{
id: user.id,
},
],
},
},
],
},
include: statusAndUserRelations,
take: limit,