mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
refactor(api): ♻️ Refactor more routes into OpenAPI-compatible formats
This commit is contained in:
parent
02cb8bcd4f
commit
bcbc9e6bf1
|
|
@ -39,7 +39,7 @@ const route = createRoute({
|
||||||
middleware: [auth(meta.auth, meta.permissions)],
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
responses: {
|
responses: {
|
||||||
200: {
|
200: {
|
||||||
description: "User blocked",
|
description: "Updated relationship",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Relationship.schema,
|
schema: Relationship.schema,
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ const route = createRoute({
|
||||||
middleware: [auth(meta.auth, meta.permissions)],
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
responses: {
|
responses: {
|
||||||
200: {
|
200: {
|
||||||
description: "User followed",
|
description: "Updated relationship",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Relationship.schema,
|
schema: Relationship.schema,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -26,23 +27,46 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "get",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}",
|
||||||
meta.route,
|
summary: "Get account data",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Gets the specified account data",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
const foundUser = await User.fromId(id);
|
200: {
|
||||||
|
description: "Account data",
|
||||||
if (!foundUser) {
|
content: {
|
||||||
return context.json({ error: "User not found" }, 404);
|
"application/json": {
|
||||||
}
|
schema: User.schema,
|
||||||
|
},
|
||||||
return context.json(foundUser.toApi(user?.id === foundUser.id));
|
},
|
||||||
},
|
},
|
||||||
),
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
|
||||||
|
const foundUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!foundUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.json(foundUser.toApi(user?.id === foundUser.id), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -39,41 +40,78 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/mute",
|
||||||
meta.route,
|
summary: "Mute user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Mute a user",
|
||||||
zValidator("json", schemas.json, handleZodError),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
auth(meta.auth, meta.permissions),
|
request: {
|
||||||
async (context) => {
|
params: schemas.param,
|
||||||
const { id } = context.req.valid("param");
|
body: {
|
||||||
const { user } = context.get("auth");
|
content: {
|
||||||
// TODO: Add duration support
|
"application/json": {
|
||||||
const { notifications } = context.req.valid("json");
|
schema: schemas.json,
|
||||||
|
},
|
||||||
if (!user) {
|
},
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
const otherUser = await User.fromId(id);
|
|
||||||
|
|
||||||
if (!otherUser) {
|
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
user,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Implement duration
|
|
||||||
await foundRelationship.update({
|
|
||||||
muting: true,
|
|
||||||
mutingNotifications: notifications ?? true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Updated relationship",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
// TODO: Add duration support
|
||||||
|
const { notifications } = context.req.valid("json");
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
user,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Implement duration
|
||||||
|
await foundRelationship.update({
|
||||||
|
muting: true,
|
||||||
|
mutingNotifications: notifications ?? true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -33,38 +34,75 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/note",
|
||||||
meta.route,
|
summary: "Set note",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Set a note on a user's profile, visible only to you",
|
||||||
zValidator("json", schemas.json, handleZodError),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
auth(meta.auth, meta.permissions),
|
request: {
|
||||||
async (context) => {
|
params: schemas.param,
|
||||||
const { id } = context.req.valid("param");
|
body: {
|
||||||
const { user } = context.get("auth");
|
content: {
|
||||||
const { comment } = context.req.valid("json");
|
"application/json": {
|
||||||
|
schema: schemas.json,
|
||||||
if (!user) {
|
},
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
},
|
||||||
}
|
|
||||||
|
|
||||||
const otherUser = await User.fromId(id);
|
|
||||||
|
|
||||||
if (!otherUser) {
|
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
user,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
await foundRelationship.update({
|
|
||||||
note: comment,
|
|
||||||
});
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Updated relationship",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
const { comment } = context.req.valid("json");
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
user,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
await foundRelationship.update({
|
||||||
|
note: comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,36 +31,67 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/pin",
|
||||||
meta.route,
|
summary: "Pin user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Pin a user to your profile",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!user) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
user,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
await foundRelationship.update({
|
|
||||||
endorsed: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
user,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
await foundRelationship.update({
|
||||||
|
endorsed: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -26,29 +27,72 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/refetch",
|
||||||
meta.route,
|
summary: "Refetch user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Refetch a user's profile from the remote server",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!user) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated user data",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: User.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newUser = await otherUser.updateFromRemote();
|
|
||||||
|
|
||||||
return context.json(newUser.toApi(false));
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "User is local",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherUser.isLocal()) {
|
||||||
|
return context.json({ error: "Cannot refetch a local user" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await otherUser.updateFromRemote();
|
||||||
|
|
||||||
|
return context.json(newUser.toApi(false), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,43 +31,74 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/remove_from_followers",
|
||||||
meta.route,
|
summary: "Remove user from followers",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Remove a user from your followers",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user: self } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!self) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const oppositeRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
otherUser,
|
|
||||||
self,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (oppositeRelationship.data.following) {
|
|
||||||
await oppositeRelationship.update({
|
|
||||||
following: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
self,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user: self } = context.get("auth");
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oppositeRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
otherUser,
|
||||||
|
self,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oppositeRelationship.data.following) {
|
||||||
|
await oppositeRelationship.update({
|
||||||
|
following: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
self,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
import {
|
import { apiRoute, applyConfig, auth, idValidator } from "@/api";
|
||||||
apiRoute,
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
applyConfig,
|
|
||||||
auth,
|
|
||||||
handleZodError,
|
|
||||||
idValidator,
|
|
||||||
} from "@/api";
|
|
||||||
import { zValidator } from "@hono/zod-validator";
|
|
||||||
import { and, eq, gt, gte, isNull, lt, sql } from "drizzle-orm";
|
import { and, eq, gt, gte, isNull, lt, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { Notes, RolePermissions } from "~/drizzle/schema";
|
import { Notes, RolePermissions } from "~/drizzle/schema";
|
||||||
|
import { Note } from "~/packages/database-interface/note";
|
||||||
import { Timeline } from "~/packages/database-interface/timeline";
|
import { Timeline } from "~/packages/database-interface/timeline";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -60,61 +56,89 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "get",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/statuses",
|
||||||
meta.route,
|
summary: "Get account statuses",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Gets an paginated list of statuses by the specified account",
|
||||||
zValidator("query", schemas.query, handleZodError),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
auth(meta.auth, meta.permissions),
|
request: {
|
||||||
async (context) => {
|
params: schemas.param,
|
||||||
const { id } = context.req.valid("param");
|
query: schemas.query,
|
||||||
const { user } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
const otherUser = await User.fromId(id);
|
200: {
|
||||||
|
description: "A list of statuses by the specified account",
|
||||||
if (!otherUser) {
|
content: {
|
||||||
return context.json({ error: "User not found" }, 404);
|
"application/json": {
|
||||||
}
|
schema: z.array(Note.schema),
|
||||||
|
|
||||||
const {
|
|
||||||
max_id,
|
|
||||||
min_id,
|
|
||||||
since_id,
|
|
||||||
limit,
|
|
||||||
exclude_reblogs,
|
|
||||||
only_media,
|
|
||||||
exclude_replies,
|
|
||||||
pinned,
|
|
||||||
} = context.req.valid("query");
|
|
||||||
|
|
||||||
const { objects, link } = await Timeline.getNoteTimeline(
|
|
||||||
and(
|
|
||||||
max_id ? lt(Notes.id, max_id) : undefined,
|
|
||||||
since_id ? gte(Notes.id, since_id) : undefined,
|
|
||||||
min_id ? gt(Notes.id, min_id) : undefined,
|
|
||||||
eq(Notes.authorId, id),
|
|
||||||
only_media
|
|
||||||
? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})`
|
|
||||||
: undefined,
|
|
||||||
pinned
|
|
||||||
? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = ${Notes.id} AND "UserToPinnedNotes"."userId" = ${otherUser.id})`
|
|
||||||
: undefined,
|
|
||||||
exclude_reblogs ? isNull(Notes.reblogId) : undefined,
|
|
||||||
exclude_replies ? isNull(Notes.replyId) : undefined,
|
|
||||||
),
|
|
||||||
limit,
|
|
||||||
context.req.url,
|
|
||||||
user?.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
return context.json(
|
|
||||||
await Promise.all(objects.map((note) => note.toApi(otherUser))),
|
|
||||||
200,
|
|
||||||
{
|
|
||||||
link,
|
|
||||||
},
|
},
|
||||||
);
|
},
|
||||||
|
headers: {
|
||||||
|
Link: {
|
||||||
|
description: "Links to the next and previous pages",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
max_id,
|
||||||
|
min_id,
|
||||||
|
since_id,
|
||||||
|
limit,
|
||||||
|
exclude_reblogs,
|
||||||
|
only_media,
|
||||||
|
exclude_replies,
|
||||||
|
pinned,
|
||||||
|
} = context.req.valid("query");
|
||||||
|
|
||||||
|
const { objects, link } = await Timeline.getNoteTimeline(
|
||||||
|
and(
|
||||||
|
max_id ? lt(Notes.id, max_id) : undefined,
|
||||||
|
since_id ? gte(Notes.id, since_id) : undefined,
|
||||||
|
min_id ? gt(Notes.id, min_id) : undefined,
|
||||||
|
eq(Notes.authorId, id),
|
||||||
|
only_media
|
||||||
|
? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})`
|
||||||
|
: undefined,
|
||||||
|
pinned
|
||||||
|
? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = ${Notes.id} AND "UserToPinnedNotes"."userId" = ${otherUser.id})`
|
||||||
|
: undefined,
|
||||||
|
exclude_reblogs ? isNull(Notes.reblogId) : undefined,
|
||||||
|
exclude_replies ? isNull(Notes.replyId) : undefined,
|
||||||
|
),
|
||||||
|
limit,
|
||||||
|
context.req.url,
|
||||||
|
user?.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return context.json(
|
||||||
|
await Promise.all(objects.map((note) => note.toApi(otherUser))),
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,38 +31,69 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/unblock",
|
||||||
meta.route,
|
summary: "Unblock user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Unblock a user",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!user) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
user,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (foundRelationship.data.blocking) {
|
|
||||||
await foundRelationship.update({
|
|
||||||
blocking: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user } = context.get("auth");
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
user,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundRelationship.data.blocking) {
|
||||||
|
await foundRelationship.update({
|
||||||
|
blocking: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,36 +31,75 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/unfollow",
|
||||||
meta.route,
|
summary: "Unfollow user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Unfollow a user",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user: self } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!self) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
self,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!(await self.unfollow(otherUser, foundRelationship))) {
|
|
||||||
return context.json({ error: "Failed to unfollow user" }, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: "Failed to unfollow user during federation",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user: self } = context.get("auth");
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
self,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!(await self.unfollow(otherUser, foundRelationship))) {
|
||||||
|
return context.json({ error: "Failed to unfollow user" }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,39 +31,70 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/unmute",
|
||||||
meta.route,
|
summary: "Unmute user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Unmute a user",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user: self } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!self) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const user = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!user) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
self,
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (foundRelationship.data.muting) {
|
|
||||||
await foundRelationship.update({
|
|
||||||
muting: false,
|
|
||||||
mutingNotifications: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user: self } = context.get("auth");
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
self,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundRelationship.data.muting) {
|
||||||
|
await foundRelationship.update({
|
||||||
|
muting: false,
|
||||||
|
mutingNotifications: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
import { apiRoute, applyConfig, auth } from "@/api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { RolePermissions } from "~/drizzle/schema";
|
import { RolePermissions } from "~/drizzle/schema";
|
||||||
import { Relationship } from "~/packages/database-interface/relationship";
|
import { Relationship } from "~/packages/database-interface/relationship";
|
||||||
import { User } from "~/packages/database-interface/user";
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,38 +31,69 @@ export const schemas = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
const route = createRoute({
|
||||||
app.on(
|
method: "post",
|
||||||
meta.allowedMethods,
|
path: "/api/v1/accounts/{id}/unpin",
|
||||||
meta.route,
|
summary: "Unpin user",
|
||||||
zValidator("param", schemas.param, handleZodError),
|
description: "Unpin a user from your profile",
|
||||||
auth(meta.auth, meta.permissions),
|
middleware: [auth(meta.auth, meta.permissions)],
|
||||||
async (context) => {
|
request: {
|
||||||
const { id } = context.req.valid("param");
|
params: schemas.param,
|
||||||
const { user: self } = context.get("auth");
|
},
|
||||||
|
responses: {
|
||||||
if (!self) {
|
200: {
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
description: "Updated relationship",
|
||||||
}
|
content: {
|
||||||
|
"application/json": {
|
||||||
const otherUser = await User.fromId(id);
|
schema: Relationship.schema,
|
||||||
|
},
|
||||||
if (!otherUser) {
|
},
|
||||||
return context.json({ error: "User not found" }, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
|
||||||
self,
|
|
||||||
otherUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (foundRelationship.data.endorsed) {
|
|
||||||
await foundRelationship.update({
|
|
||||||
endorsed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.json(foundRelationship.toApi());
|
|
||||||
},
|
},
|
||||||
),
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute((app) =>
|
||||||
|
app.openapi(route, async (context) => {
|
||||||
|
const { id } = context.req.valid("param");
|
||||||
|
const { user: self } = context.get("auth");
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherUser = await User.fromId(id);
|
||||||
|
|
||||||
|
if (!otherUser) {
|
||||||
|
return context.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundRelationship = await Relationship.fromOwnerAndSubject(
|
||||||
|
self,
|
||||||
|
otherUser,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundRelationship.data.endorsed) {
|
||||||
|
await foundRelationship.update({
|
||||||
|
endorsed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.json(foundRelationship.toApi(), 200);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
4
app.ts
4
app.ts
|
|
@ -2,6 +2,7 @@ import { sentry } from "@/sentry";
|
||||||
import { cors } from "@hono/hono/cors";
|
import { cors } from "@hono/hono/cors";
|
||||||
import { prettyJSON } from "@hono/hono/pretty-json";
|
import { prettyJSON } from "@hono/hono/pretty-json";
|
||||||
import { secureHeaders } from "@hono/hono/secure-headers";
|
import { secureHeaders } from "@hono/hono/secure-headers";
|
||||||
|
import { swaggerUI } from "@hono/swagger-ui";
|
||||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||||
/* import { prometheus } from "@hono/prometheus";
|
/* import { prometheus } from "@hono/prometheus";
|
||||||
*/ import { getLogger } from "@logtape/logtape";
|
*/ import { getLogger } from "@logtape/logtape";
|
||||||
|
|
@ -13,14 +14,15 @@ import { boundaryCheck } from "./middlewares/boundary-check";
|
||||||
import { ipBans } from "./middlewares/ip-bans";
|
import { ipBans } from "./middlewares/ip-bans";
|
||||||
import { logger } from "./middlewares/logger";
|
import { logger } from "./middlewares/logger";
|
||||||
import { routes } from "./routes";
|
import { routes } from "./routes";
|
||||||
import { swaggerUI } from "@hono/swagger-ui";
|
|
||||||
import type { ApiRouteExports, HonoEnv } from "./types/api";
|
import type { ApiRouteExports, HonoEnv } from "./types/api";
|
||||||
|
import { handleZodError } from "@/api";
|
||||||
|
|
||||||
export const appFactory = async () => {
|
export const appFactory = async () => {
|
||||||
const serverLogger = getLogger("server");
|
const serverLogger = getLogger("server");
|
||||||
|
|
||||||
const app = new OpenAPIHono<HonoEnv>({
|
const app = new OpenAPIHono<HonoEnv>({
|
||||||
strict: false,
|
strict: false,
|
||||||
|
defaultHook: handleZodError,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* const { printMetrics, registerMetrics } = prometheus({
|
/* const { printMetrics, registerMetrics } = prometheus({
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
eq,
|
eq,
|
||||||
inArray,
|
inArray,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
|
import { z } from "zod";
|
||||||
import { db } from "~/drizzle/db";
|
import { db } from "~/drizzle/db";
|
||||||
import { Attachments } from "~/drizzle/schema";
|
import { Attachments } from "~/drizzle/schema";
|
||||||
import { MediaBackendType } from "~/packages/config-manager/config.type";
|
import { MediaBackendType } from "~/packages/config-manager/config.type";
|
||||||
|
|
@ -21,6 +22,34 @@ import { BaseInterface } from "./base";
|
||||||
export type AttachmentType = InferSelectModel<typeof Attachments>;
|
export type AttachmentType = InferSelectModel<typeof Attachments>;
|
||||||
|
|
||||||
export class Attachment extends BaseInterface<typeof Attachments> {
|
export class Attachment extends BaseInterface<typeof Attachments> {
|
||||||
|
static schema: z.ZodType<ApiAttachment> = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
type: z.enum(["unknown", "image", "gifv", "video", "audio"]),
|
||||||
|
url: z.string().url(),
|
||||||
|
remote_url: z.string().url().nullable(),
|
||||||
|
preview_url: z.string().url().nullable(),
|
||||||
|
text_url: z.string().url().nullable(),
|
||||||
|
meta: z
|
||||||
|
.object({
|
||||||
|
width: z.number().optional(),
|
||||||
|
height: z.number().optional(),
|
||||||
|
fps: z.number().optional(),
|
||||||
|
size: z.string().optional(),
|
||||||
|
duration: z.number().optional(),
|
||||||
|
length: z.string().optional(),
|
||||||
|
aspect: z.number().optional(),
|
||||||
|
original: z.object({
|
||||||
|
width: z.number().optional(),
|
||||||
|
height: z.number().optional(),
|
||||||
|
size: z.string().optional(),
|
||||||
|
aspect: z.number().optional(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
blurhash: z.string().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
async reload(): Promise<void> {
|
async reload(): Promise<void> {
|
||||||
const reloaded = await Attachment.fromId(this.data.id);
|
const reloaded = await Attachment.fromId(this.data.id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import {
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import { createRegExp, exactly, global } from "magic-regexp";
|
import { createRegExp, exactly, global } from "magic-regexp";
|
||||||
|
import { z } from "zod";
|
||||||
import {
|
import {
|
||||||
type Application,
|
type Application,
|
||||||
applicationToApi,
|
applicationToApi,
|
||||||
|
|
@ -56,6 +57,96 @@ import { User } from "./user";
|
||||||
* Gives helpers to fetch notes from database in a nice format
|
* Gives helpers to fetch notes from database in a nice format
|
||||||
*/
|
*/
|
||||||
export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
||||||
|
static schema: z.ZodType<ApiStatus> = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
uri: z.string().url(),
|
||||||
|
url: z.string().url(),
|
||||||
|
account: z.lazy(() => User.schema),
|
||||||
|
in_reply_to_id: z.string().uuid().nullable(),
|
||||||
|
in_reply_to_account_id: z.string().uuid().nullable(),
|
||||||
|
reblog: z.lazy(() => Note.schema).nullable(),
|
||||||
|
content: z.string(),
|
||||||
|
plain_content: z.string().nullable(),
|
||||||
|
created_at: z.string(),
|
||||||
|
edited_at: z.string().nullable(),
|
||||||
|
emojis: z.array(Emoji.schema),
|
||||||
|
replies_count: z.number().int().nonnegative(),
|
||||||
|
reblogs_count: z.number().int().nonnegative(),
|
||||||
|
favourites_count: z.number().int().nonnegative(),
|
||||||
|
reblogged: z.boolean().nullable(),
|
||||||
|
favourited: z.boolean().nullable(),
|
||||||
|
muted: z.boolean().nullable(),
|
||||||
|
sensitive: z.boolean(),
|
||||||
|
spoiler_text: z.string(),
|
||||||
|
visibility: z.enum(["public", "unlisted", "private", "direct"]),
|
||||||
|
media_attachments: z.array(Attachment.schema),
|
||||||
|
mentions: z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
username: z.string(),
|
||||||
|
acct: z.string(),
|
||||||
|
url: z.string().url(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
tags: z.array(z.object({ name: z.string(), url: z.string().url() })),
|
||||||
|
card: z
|
||||||
|
.object({
|
||||||
|
url: z.string().url(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
type: z.enum(["link", "photo", "video", "rich"]),
|
||||||
|
image: z.string().url().nullable(),
|
||||||
|
author_name: z.string().nullable(),
|
||||||
|
author_url: z.string().url().nullable(),
|
||||||
|
provider_name: z.string().nullable(),
|
||||||
|
provider_url: z.string().url().nullable(),
|
||||||
|
html: z.string().nullable(),
|
||||||
|
width: z.number().int().nonnegative().nullable(),
|
||||||
|
height: z.number().int().nonnegative().nullable(),
|
||||||
|
embed_url: z.string().url().nullable(),
|
||||||
|
blurhash: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
poll: z
|
||||||
|
.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
expires_at: z.string(),
|
||||||
|
expired: z.boolean(),
|
||||||
|
multiple: z.boolean(),
|
||||||
|
votes_count: z.number().int().nonnegative(),
|
||||||
|
voted: z.boolean(),
|
||||||
|
options: z.array(
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
votes_count: z.number().int().nonnegative().nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
application: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
website: z.string().url().nullable().optional(),
|
||||||
|
vapid_key: z.string().nullable().optional(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
language: z.string().nullable(),
|
||||||
|
pinned: z.boolean().nullable(),
|
||||||
|
emoji_reactions: z.array(
|
||||||
|
z.object({
|
||||||
|
count: z.number().int().nonnegative(),
|
||||||
|
me: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
url: z.string().url().optional(),
|
||||||
|
static_url: z.string().url().optional(),
|
||||||
|
accounts: z.array(z.lazy(() => User.schema)).optional(),
|
||||||
|
account_ids: z.array(z.string().uuid()).optional(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
quote: z.lazy(() => Note.schema).nullable(),
|
||||||
|
bookmarked: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
save(): Promise<StatusWithRelations> {
|
save(): Promise<StatusWithRelations> {
|
||||||
return this.update(this.data);
|
return this.update(this.data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ import { Role } from "./role";
|
||||||
* Gives helpers to fetch users from database in a nice format
|
* Gives helpers to fetch users from database in a nice format
|
||||||
*/
|
*/
|
||||||
export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
||||||
static schema = z.object({
|
static schema: z.ZodType<ApiAccount> = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
acct: z.string(),
|
acct: z.string(),
|
||||||
|
|
@ -92,12 +92,12 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
verified: z.boolean().nullable().optional(),
|
verified: z.boolean().optional(),
|
||||||
verified_at: z.string().nullable().optional(),
|
verified_at: z.string().nullable().optional(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// FIXME: Use a proper type
|
// FIXME: Use a proper type
|
||||||
moved: z.any().nullable(),
|
moved: z.lazy(() => User.schema).nullable(),
|
||||||
bot: z.boolean().nullable(),
|
bot: z.boolean().nullable(),
|
||||||
source: z
|
source: z
|
||||||
.object({
|
.object({
|
||||||
|
|
@ -105,6 +105,12 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
||||||
sensitive: z.boolean().nullable(),
|
sensitive: z.boolean().nullable(),
|
||||||
language: z.string().nullable(),
|
language: z.string().nullable(),
|
||||||
note: z.string(),
|
note: z.string(),
|
||||||
|
fields: z.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
role: z
|
role: z
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue