mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
Add reblog and unreblog endpoints
This commit is contained in:
parent
5bba96435c
commit
ca94c35bc4
9 changed files with 368 additions and 7 deletions
43
server/api/api/v1/profile/avatar.ts
Normal file
43
server/api/api/v1/profile/avatar.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
import {
|
||||
getFromRequest,
|
||||
userRelations,
|
||||
userToAPI,
|
||||
} from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
allowedMethods: ["DELETE"],
|
||||
ratelimits: {
|
||||
max: 10,
|
||||
duration: 60,
|
||||
},
|
||||
route: "/api/v1/profile/avatar",
|
||||
auth: {
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Deletes a user avatar
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user } = await getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
// Delete user avatar
|
||||
const newUser = await client.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
avatar: "",
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
|
||||
return jsonResponse(await userToAPI(newUser));
|
||||
};
|
||||
43
server/api/api/v1/profile/header.ts
Normal file
43
server/api/api/v1/profile/header.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
import {
|
||||
getFromRequest,
|
||||
userRelations,
|
||||
userToAPI,
|
||||
} from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
allowedMethods: ["DELETE"],
|
||||
ratelimits: {
|
||||
max: 10,
|
||||
duration: 60,
|
||||
},
|
||||
route: "/api/v1/profile/header",
|
||||
auth: {
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Deletes a user header
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user } = await getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
// Delete user header
|
||||
const newUser = await client.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
header: "",
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
|
||||
return jsonResponse(await userToAPI(newUser));
|
||||
};
|
||||
95
server/api/api/v1/statuses/[id]/reblog.ts
Normal file
95
server/api/api/v1/statuses/[id]/reblog.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { client } from "~database/datasource";
|
||||
import {
|
||||
isViewableByUser,
|
||||
statusAndUserRelations,
|
||||
statusToAPI,
|
||||
} from "~database/entities/Status";
|
||||
import { getFromRequest } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
ratelimits: {
|
||||
max: 100,
|
||||
duration: 60,
|
||||
},
|
||||
route: "/api/v1/statuses/:id/reblog",
|
||||
auth: {
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Reblogs a post
|
||||
*/
|
||||
export default async (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
const config = getConfig();
|
||||
|
||||
const { user } = await getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const { visibility = "public" } = await parseRequest<{
|
||||
visibility: "public" | "unlisted" | "private";
|
||||
}>(req);
|
||||
|
||||
const status = await client.status.findUnique({
|
||||
where: { id },
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
||||
// Check if user is authorized to view this status (if it's private)
|
||||
if (!status || !isViewableByUser(status, user))
|
||||
return errorResponse("Record not found", 404);
|
||||
|
||||
const existingReblog = await client.status.findFirst({
|
||||
where: {
|
||||
authorId: user.id,
|
||||
reblogId: status.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingReblog) {
|
||||
return errorResponse("Already reblogged", 422);
|
||||
}
|
||||
|
||||
const newReblog = await client.status.create({
|
||||
data: {
|
||||
authorId: user.id,
|
||||
reblogId: status.id,
|
||||
isReblog: true,
|
||||
uri: `${config.http.base_url}/statuses/FAKE-${crypto.randomUUID()}`,
|
||||
visibility,
|
||||
sensitive: false,
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
||||
await client.status.update({
|
||||
where: { id: newReblog.id },
|
||||
data: {
|
||||
uri: `${config.http.base_url}/statuses/${newReblog.id}`,
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
||||
return jsonResponse(
|
||||
await statusToAPI(
|
||||
{
|
||||
...newReblog,
|
||||
uri: `${config.http.base_url}/statuses/${newReblog.id}`,
|
||||
},
|
||||
user
|
||||
)
|
||||
);
|
||||
};
|
||||
69
server/api/api/v1/statuses/[id]/unreblog.ts
Normal file
69
server/api/api/v1/statuses/[id]/unreblog.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { client } from "~database/datasource";
|
||||
import {
|
||||
isViewableByUser,
|
||||
statusAndUserRelations,
|
||||
statusToAPI,
|
||||
} from "~database/entities/Status";
|
||||
import { getFromRequest } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
import { APIStatus } from "~types/entities/status";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
ratelimits: {
|
||||
max: 100,
|
||||
duration: 60,
|
||||
},
|
||||
route: "/api/v1/statuses/:id/unreblog",
|
||||
auth: {
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Unreblogs 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);
|
||||
|
||||
const status = await client.status.findUnique({
|
||||
where: { id },
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
||||
// Check if user is authorized to view this status (if it's private)
|
||||
if (!status || !isViewableByUser(status, user))
|
||||
return errorResponse("Record not found", 404);
|
||||
|
||||
const existingReblog = await client.status.findFirst({
|
||||
where: {
|
||||
authorId: user.id,
|
||||
reblogId: status.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingReblog) {
|
||||
return errorResponse("Not already reblogged", 422);
|
||||
}
|
||||
|
||||
await client.status.delete({
|
||||
where: { id: existingReblog.id },
|
||||
});
|
||||
|
||||
return jsonResponse({
|
||||
...(await statusToAPI(status, user)),
|
||||
reblogged: false,
|
||||
reblogs_count: status._count.reblogs - 1,
|
||||
} as APIStatus);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue