Add status pinning and unpinning, fix bugs

This commit is contained in:
Jesse Wierzbinski 2023-11-26 14:56:16 -10:00
parent 0a74bbfe93
commit f51476e810
No known key found for this signature in database
14 changed files with 322 additions and 116 deletions

View file

@ -32,13 +32,14 @@ export default async (
max_id,
min_id,
since_id,
limit,
limit = "20",
exclude_reblogs,
pinned,
}: {
max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
limit?: string;
only_media?: boolean;
exclude_replies?: boolean;
exclude_reblogs?: boolean;
@ -54,6 +55,48 @@ export default async (
if (!user) return errorResponse("User not found", 404);
if (pinned) {
const objects = await client.status.findMany({
where: {
authorId: id,
isReblog: false,
pinnedBy: {
some: {
id: user.id,
},
},
id: {
lt: max_id,
gt: min_id,
gte: since_id,
},
},
include: statusAndUserRelations,
take: Number(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(
await Promise.all(objects.map(status => statusToAPI(status, user))),
200,
{
Link: linkHeader.join(", "),
}
);
}
const objects = await client.status.findMany({
where: {
authorId: id,
@ -65,7 +108,7 @@ export default async (
},
},
include: statusAndUserRelations,
take: limit ?? 20,
take: Number(limit),
orderBy: {
id: "desc",
},
@ -76,11 +119,8 @@ export default async (
if (objects.length > 0) {
const urlWithoutQuery = req.url.split("?")[0];
linkHeader.push(
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`
);
linkHeader.push(
`<${urlWithoutQuery}?since_id=${objects.at(-1)
?.id}&limit=${limit}>; rel="prev"`
`<${urlWithoutQuery}?max_id=${objects.at(-1)?.id}>; rel="next"`,
`<${urlWithoutQuery}?min_id=${objects[0].id}>; rel="prev"`
);
}

View file

@ -27,7 +27,7 @@ export default async (req: Request): Promise<Response> => {
const blocks = await client.user.findMany({
where: {
relationshipSubjects: {
every: {
some: {
ownerId: user.id,
blocking: true,
},

View file

@ -27,7 +27,7 @@ export default async (req: Request): Promise<Response> => {
const blocks = await client.user.findMany({
where: {
relationshipSubjects: {
every: {
some: {
ownerId: user.id,
muting: true,
},

View file

@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 100,
duration: 60,
},
route: "/api/v1/statuses/:id/pin",
auth: {
required: true,
},
});
/**
* Pin a post
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);
let status = await client.status.findUnique({
where: { id },
include: statusAndUserRelations,
});
// Check if status exists
if (!status) return errorResponse("Record not found", 404);
// Check if status is user's
if (status.authorId !== user.id) return errorResponse("Unauthorized", 401);
await client.user.update({
where: { id: user.id },
data: {
pinnedNotes: {
connect: {
id: status.id,
},
},
},
});
status = await client.status.findUnique({
where: { id },
include: statusAndUserRelations,
});
if (!status) return errorResponse("Record not found", 404);
return jsonResponse(statusToAPI(status, user));
};

View file

@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 100,
duration: 60,
},
route: "/api/v1/statuses/:id/unpin",
auth: {
required: true,
},
});
/**
* Unpins a post
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);
let status = await client.status.findUnique({
where: { id },
include: statusAndUserRelations,
});
// Check if status exists
if (!status) return errorResponse("Record not found", 404);
// Check if status is user's
if (status.authorId !== user.id) return errorResponse("Unauthorized", 401);
await client.user.update({
where: { id: user.id },
data: {
pinnedNotes: {
disconnect: {
id: status.id,
},
},
},
});
status = await client.status.findUnique({
where: { id },
include: statusAndUserRelations,
});
if (!status) return errorResponse("Record not found", 404);
return jsonResponse(statusToAPI(status, user));
};

View file

@ -58,8 +58,8 @@ export default async (req: Request): Promise<Response> => {
not: null,
}
: local
? null
: undefined,
? null
: undefined,
},
include: statusAndUserRelations,
take: limit,
@ -73,12 +73,8 @@ export default async (req: Request): Promise<Response> => {
if (objects.length > 0) {
const urlWithoutQuery = req.url.split("?")[0];
linkHeader.push(
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`
);
linkHeader.push(
`<${urlWithoutQuery}?since_id=${
objects[objects.length - 1].id
}&limit=${limit}>; rel="prev"`
`<${urlWithoutQuery}?max_id=${objects.at(-1)?.id}>; rel="next"`,
`<${urlWithoutQuery}?min_id=${objects[0].id}>; rel="prev"`
);
}

View file

@ -54,7 +54,10 @@ export default async (req: Request): Promise<Response> => {
);
}
if (!config.validation.allowed_mime_types.includes(file.type)) {
if (
config.validation.enforce_mime_types &&
!config.validation.allowed_mime_types.includes(file.type)
) {
return errorResponse("Invalid file type", 415);
}