mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
Add status pinning and unpinning, fix bugs
This commit is contained in:
parent
0a74bbfe93
commit
f51476e810
14 changed files with 322 additions and 116 deletions
|
|
@ -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"`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
65
server/api/api/v1/statuses/[id]/pin.ts
Normal file
65
server/api/api/v1/statuses/[id]/pin.ts
Normal 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));
|
||||
};
|
||||
65
server/api/api/v1/statuses/[id]/unpin.ts
Normal file
65
server/api/api/v1/statuses/[id]/unpin.ts
Normal 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));
|
||||
};
|
||||
|
|
@ -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"`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue