mirror of
https://github.com/versia-pub/server.git
synced 2026-01-26 12:16:01 +01:00
refactor(api): 🚚 Refactor authentication middleware and implement some OpenAPI routes
This commit is contained in:
parent
edf5edca9f
commit
1ab1c68d36
|
|
@ -1,6 +1,6 @@
|
|||
import { apiRoute, applyConfig, handleZodError } from "@/api";
|
||||
import { apiRoute, applyConfig } from "@/api";
|
||||
import { redirect } from "@/response";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { createRoute } from "@hono/zod-openapi";
|
||||
import { eq, or } from "drizzle-orm";
|
||||
import { SignJWT } from "jose";
|
||||
import { z } from "zod";
|
||||
|
|
@ -59,6 +59,34 @@ export const schemas = {
|
|||
}),
|
||||
};
|
||||
|
||||
const route = createRoute({
|
||||
method: "post",
|
||||
path: "/api/auth/login",
|
||||
summary: "Login",
|
||||
description: "Login to the application",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"multipart/form-data": {
|
||||
schema: schemas.form,
|
||||
},
|
||||
},
|
||||
},
|
||||
query: schemas.query,
|
||||
},
|
||||
responses: {
|
||||
302: {
|
||||
description: "Redirect to OAuth authorize, or error",
|
||||
headers: {
|
||||
"Set-Cookie": {
|
||||
description: "JWT cookie",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const returnError = (query: object, error: string, description: string) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
|
|
@ -81,12 +109,7 @@ const returnError = (query: object, error: string, description: string) => {
|
|||
};
|
||||
|
||||
export default apiRoute((app) =>
|
||||
app.on(
|
||||
meta.allowedMethods,
|
||||
meta.route,
|
||||
zValidator("form", schemas.form, handleZodError),
|
||||
zValidator("query", schemas.query, handleZodError),
|
||||
async (context) => {
|
||||
app.openapi(route, async (context) => {
|
||||
if (config.oidc.forced) {
|
||||
return returnError(
|
||||
context.req.query(),
|
||||
|
|
@ -109,10 +132,7 @@ export default apiRoute((app) =>
|
|||
if (
|
||||
!(
|
||||
user &&
|
||||
(await Bun.password.verify(
|
||||
password,
|
||||
user.data.password || "",
|
||||
))
|
||||
(await Bun.password.verify(password, user.data.password || ""))
|
||||
)
|
||||
) {
|
||||
return returnError(
|
||||
|
|
@ -124,12 +144,12 @@ export default apiRoute((app) =>
|
|||
|
||||
if (user.data.passwordResetToken) {
|
||||
return redirect(
|
||||
`${
|
||||
config.frontend.routes.password_reset
|
||||
}?${new URLSearchParams({
|
||||
`${config.frontend.routes.password_reset}?${new URLSearchParams(
|
||||
{
|
||||
token: user.data.passwordResetToken ?? "",
|
||||
login_reset: "true",
|
||||
}).toString()}`,
|
||||
},
|
||||
).toString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -172,11 +192,7 @@ export default apiRoute((app) =>
|
|||
|
||||
// Add all data that is not undefined except email and password
|
||||
for (const [key, value] of Object.entries(context.req.query())) {
|
||||
if (
|
||||
key !== "email" &&
|
||||
key !== "password" &&
|
||||
value !== undefined
|
||||
) {
|
||||
if (key !== "email" && key !== "password" && value !== undefined) {
|
||||
searchParams.append(key, String(value));
|
||||
}
|
||||
}
|
||||
|
|
@ -191,6 +207,5 @@ export default apiRoute((app) =>
|
|||
}`,
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { apiRoute, applyConfig, handleZodError } from "@/api";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { apiRoute, applyConfig } from "@/api";
|
||||
import { createRoute } from "@hono/zod-openapi";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { db } from "~/drizzle/db";
|
||||
|
|
@ -26,17 +26,29 @@ export const schemas = {
|
|||
}),
|
||||
};
|
||||
|
||||
const route = createRoute({
|
||||
method: "get",
|
||||
path: "/api/auth/redirect",
|
||||
summary: "OAuth Code flow",
|
||||
description:
|
||||
"Redirects to the application, or back to login if the code is invalid",
|
||||
responses: {
|
||||
302: {
|
||||
description:
|
||||
"Redirects to the application, or back to login if the code is invalid",
|
||||
},
|
||||
},
|
||||
request: {
|
||||
query: schemas.query,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* OAuth Code flow
|
||||
*/
|
||||
export default apiRoute((app) =>
|
||||
app.on(
|
||||
meta.allowedMethods,
|
||||
meta.route,
|
||||
zValidator("query", schemas.query, handleZodError),
|
||||
async (context) => {
|
||||
const { redirect_uri, client_id, code } =
|
||||
context.req.valid("query");
|
||||
app.openapi(route, async (context) => {
|
||||
const { redirect_uri, client_id, code } = context.req.valid("query");
|
||||
|
||||
const redirectToLogin = (error: string) =>
|
||||
Response.redirect(
|
||||
|
|
@ -50,10 +62,7 @@ export default apiRoute((app) =>
|
|||
const foundToken = await db
|
||||
.select()
|
||||
.from(Tokens)
|
||||
.leftJoin(
|
||||
Applications,
|
||||
eq(Tokens.applicationId, Applications.id),
|
||||
)
|
||||
.leftJoin(Applications, eq(Tokens.applicationId, Applications.id))
|
||||
.where(
|
||||
and(
|
||||
eq(Tokens.code, code),
|
||||
|
|
@ -68,6 +77,5 @@ export default apiRoute((app) =>
|
|||
|
||||
// Redirect back to application
|
||||
return Response.redirect(`${redirect_uri}?code=${code}`, 302);
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { apiRoute, applyConfig, handleZodError } from "@/api";
|
||||
import { apiRoute, applyConfig } from "@/api";
|
||||
import { response } from "@/response";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { createRoute } from "@hono/zod-openapi";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { Users } from "~/drizzle/schema";
|
||||
|
|
@ -26,6 +26,30 @@ export const schemas = {
|
|||
}),
|
||||
};
|
||||
|
||||
const route = createRoute({
|
||||
method: "post",
|
||||
path: "/api/auth/reset",
|
||||
summary: "Reset password",
|
||||
description: "Reset password",
|
||||
responses: {
|
||||
302: {
|
||||
description: "Redirect to the password reset page with a message",
|
||||
},
|
||||
},
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/x-www-form-urlencoded": {
|
||||
schema: schemas.form,
|
||||
},
|
||||
"multipart/form-data": {
|
||||
schema: schemas.form,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const returnError = (token: string, error: string, description: string) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
|
|
@ -44,16 +68,10 @@ const returnError = (token: string, error: string, description: string) => {
|
|||
};
|
||||
|
||||
export default apiRoute((app) =>
|
||||
app.on(
|
||||
meta.allowedMethods,
|
||||
meta.route,
|
||||
zValidator("form", schemas.form, handleZodError),
|
||||
async (context) => {
|
||||
app.openapi(route, async (context) => {
|
||||
const { token, password } = context.req.valid("form");
|
||||
|
||||
const user = await User.fromSql(
|
||||
eq(Users.passwordResetToken, token),
|
||||
);
|
||||
const user = await User.fromSql(eq(Users.passwordResetToken, token));
|
||||
|
||||
if (!user) {
|
||||
return returnError(token, "invalid_token", "Invalid token");
|
||||
|
|
@ -67,6 +85,5 @@ export default apiRoute((app) =>
|
|||
return response(null, 302, {
|
||||
Location: `${config.frontend.routes.password_reset}?success=true`,
|
||||
});
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { apiRoute, applyConfig, auth, handleZodError } from "@/api";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { apiRoute, applyConfig, auth } from "@/api";
|
||||
import { createRoute } from "@hono/zod-openapi";
|
||||
import { z } from "zod";
|
||||
import { RolePermissions } from "~/drizzle/schema";
|
||||
import { Relationship } from "~/packages/database-interface/relationship";
|
||||
import { User } from "~/packages/database-interface/user";
|
||||
import { ErrorSchema } from "~/types/api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -30,15 +31,47 @@ export const schemas = {
|
|||
}),
|
||||
};
|
||||
|
||||
const route = createRoute({
|
||||
method: "post",
|
||||
path: "/api/v1/accounts/{id}/block",
|
||||
summary: "Block user",
|
||||
description: "Block a user",
|
||||
middleware: [auth(meta.auth, meta.permissions)],
|
||||
responses: {
|
||||
200: {
|
||||
description: "User blocked",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Relationship.schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
401: {
|
||||
description: "Unauthorized",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: ErrorSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
404: {
|
||||
description: "User not found",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: ErrorSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
params: schemas.param,
|
||||
},
|
||||
});
|
||||
|
||||
export default apiRoute((app) =>
|
||||
app.on(
|
||||
meta.allowedMethods,
|
||||
meta.route,
|
||||
zValidator("param", schemas.param, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
app.openapi(route, async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
@ -61,7 +94,6 @@ export default apiRoute((app) =>
|
|||
});
|
||||
}
|
||||
|
||||
return context.json(foundRelationship.toApi());
|
||||
},
|
||||
),
|
||||
return context.json(foundRelationship.toApi(), 200);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
const { reblogs, notify, languages } = context.req.valid("json");
|
||||
|
||||
if (!user) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const foundUser = await User.fromId(id);
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
// TODO: Add duration support
|
||||
const { notifications } = context.req.valid("json");
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
const { comment } = context.req.valid("json");
|
||||
|
||||
if (!user) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const otherUser = await User.fromId(id);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute((app) =>
|
|||
zValidator("query", schemas.query, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
const { id: ids } = context.req.valid("query");
|
||||
|
||||
if (!self) {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { acct } = context.req.valid("query");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!acct) {
|
||||
return context.json({ error: "Invalid acct parameter" }, 400);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
zValidator("query", schemas.query, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
const { id } = context.req.valid("query");
|
||||
|
||||
const ids = Array.isArray(id) ? id : [id];
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { q, limit, offset, resolve, following } =
|
||||
context.req.valid("query");
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self && following) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export default apiRoute((app) =>
|
|||
zValidator("json", schemas.json, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
const {
|
||||
display_name,
|
||||
username,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
(context) => {
|
||||
// TODO: Add checks for disabled/unverified accounts
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user, token } = context.req.valid("header");
|
||||
const { user, token } = context.get("auth");
|
||||
|
||||
if (!token) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export default apiRoute((app) =>
|
|||
const { max_id, since_id, min_id, limit } =
|
||||
context.req.valid("query");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const emojis = await Emoji.manyFromSql(
|
||||
and(
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { shortcode, element, alt, global, category } =
|
||||
context.req.valid("json");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default apiRoute((app) =>
|
|||
const { max_id, since_id, min_id, limit } =
|
||||
context.req.valid("query");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
zValidator("param", schemas.param, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
zValidator("param", schemas.param, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default apiRoute((app) =>
|
|||
const { max_id, since_id, min_id, limit } =
|
||||
context.req.valid("query");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { "timeline[]": timelines } = context.req.valid("query");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const timeline = Array.isArray(timelines) ? timelines : [];
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { max_id, since_id, limit, min_id } =
|
||||
context.req.valid("query");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
zValidator("query", schemas.query, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export default apiRoute((app) =>
|
|||
zValidator("query", schemas.query, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
|
||||
if (!self) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute((app) =>
|
|||
zValidator("param", schemas.param, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
if (!user) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export default apiRoute((app) =>
|
|||
meta.route,
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id: issuerId } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const form = context.req.valid("json");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const foundStatus = await Note.fromId(id, user?.id);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default apiRoute((app) =>
|
|||
context.req.valid("query");
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
// TODO: Polls
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { visibility } = context.req.valid("json");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default apiRoute((app) =>
|
|||
const { id } = context.req.valid("param");
|
||||
const { max_id, min_id, since_id, limit } =
|
||||
context.req.valid("query");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default apiRoute((app) =>
|
|||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { id } = context.req.valid("param");
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default apiRoute((app) =>
|
|||
zValidator("json", schemas.json, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user, application } = context.req.valid("header");
|
||||
const { user, application } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default apiRoute((app) =>
|
|||
const { max_id, since_id, min_id, limit } =
|
||||
context.req.valid("query");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export default apiRoute((app) =>
|
|||
only_media,
|
||||
} = context.req.valid("query");
|
||||
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export default apiRoute((app) =>
|
|||
zValidator("json", schemas.json, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
const { id } = context.req.valid("param");
|
||||
|
||||
if (!user) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default apiRoute((app) =>
|
|||
zValidator("json", schemas.json, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user } = context.req.valid("header");
|
||||
const { user } = context.get("auth");
|
||||
|
||||
if (!user) {
|
||||
return context.json({ error: "Unauthorized" }, 401);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export default apiRoute((app) =>
|
|||
zValidator("query", schemas.query, handleZodError),
|
||||
auth(meta.auth, meta.permissions),
|
||||
async (context) => {
|
||||
const { user: self } = context.req.valid("header");
|
||||
const { user: self } = context.get("auth");
|
||||
const { q, type, resolve, following, account_id, limit, offset } =
|
||||
context.req.valid("query");
|
||||
|
||||
|
|
|
|||
4
app.ts
4
app.ts
|
|
@ -12,12 +12,12 @@ import { boundaryCheck } from "./middlewares/boundary-check";
|
|||
import { ipBans } from "./middlewares/ip-bans";
|
||||
import { logger } from "./middlewares/logger";
|
||||
import { routes } from "./routes";
|
||||
import type { ApiRouteExports } from "./types/api";
|
||||
import type { ApiRouteExports, HonoEnv } from "./types/api";
|
||||
|
||||
export const appFactory = async () => {
|
||||
const serverLogger = getLogger("server");
|
||||
|
||||
const app = new OpenAPIHono({
|
||||
const app = new OpenAPIHono<HonoEnv>({
|
||||
strict: false,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
eq,
|
||||
inArray,
|
||||
} from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { db } from "~/drizzle/db";
|
||||
import { Relationships } from "~/drizzle/schema";
|
||||
import { BaseInterface } from "./base";
|
||||
|
|
@ -25,6 +26,23 @@ export class Relationship extends BaseInterface<
|
|||
typeof Relationships,
|
||||
RelationshipWithOpposite
|
||||
> {
|
||||
static schema = z.object({
|
||||
id: z.string(),
|
||||
blocked_by: z.boolean(),
|
||||
blocking: z.boolean(),
|
||||
domain_blocking: z.boolean(),
|
||||
endorsed: z.boolean(),
|
||||
followed_by: z.boolean(),
|
||||
following: z.boolean(),
|
||||
muting_notifications: z.boolean(),
|
||||
muting: z.boolean(),
|
||||
note: z.string().nullable(),
|
||||
notifying: z.boolean(),
|
||||
requested_by: z.boolean(),
|
||||
requested: z.boolean(),
|
||||
showing_reblogs: z.boolean(),
|
||||
});
|
||||
|
||||
async reload(): Promise<void> {
|
||||
const reloaded = await Relationship.fromId(this.data.id);
|
||||
|
||||
|
|
|
|||
20
types/api.ts
20
types/api.ts
|
|
@ -11,8 +11,10 @@ import type {
|
|||
Unfollow,
|
||||
User,
|
||||
} from "@versia/federation/types";
|
||||
import type { z } from "zod";
|
||||
import { z } from "zod";
|
||||
import type { Application } from "~/classes/functions/application";
|
||||
import type { RolePermissions } from "~/drizzle/schema";
|
||||
import type { User as DatabaseUser } from "~/packages/database-interface/user";
|
||||
|
||||
export type HttpVerb = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
||||
export interface ApiRouteMetadata {
|
||||
|
|
@ -43,13 +45,27 @@ export interface ApiRouteMetadata {
|
|||
};
|
||||
}
|
||||
|
||||
export const ErrorSchema = z.object({
|
||||
error: z.string(),
|
||||
});
|
||||
|
||||
export type HonoEnv = {
|
||||
Variables: {
|
||||
auth: {
|
||||
user: DatabaseUser | null;
|
||||
token: string | null;
|
||||
application: Application | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export interface ApiRouteExports {
|
||||
meta: ApiRouteMetadata;
|
||||
schemas?: {
|
||||
query?: z.AnyZodObject;
|
||||
body?: z.AnyZodObject;
|
||||
};
|
||||
default: (app: OpenAPIHono) => RouterRoute;
|
||||
default: (app: OpenAPIHono<HonoEnv>) => RouterRoute;
|
||||
}
|
||||
|
||||
export type KnownEntity =
|
||||
|
|
|
|||
33
utils/api.ts
33
utils/api.ts
|
|
@ -1,6 +1,5 @@
|
|||
import type { Context } from "@hono/hono";
|
||||
import { createMiddleware } from "@hono/hono/factory";
|
||||
import { validator } from "@hono/hono/validator";
|
||||
import type { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { extractParams, verifySolution } from "altcha-lib";
|
||||
|
|
@ -29,7 +28,7 @@ import { db } from "~/drizzle/db";
|
|||
import { Challenges } from "~/drizzle/schema";
|
||||
import { config } from "~/packages/config-manager/index";
|
||||
import type { User } from "~/packages/database-interface/user";
|
||||
import type { ApiRouteMetadata, HttpVerb } from "~/types/api";
|
||||
import type { ApiRouteMetadata, HonoEnv, HttpVerb } from "~/types/api";
|
||||
|
||||
export const applyConfig = (routeMeta: ApiRouteMetadata) => {
|
||||
const newMeta = routeMeta;
|
||||
|
|
@ -45,13 +44,7 @@ export const applyConfig = (routeMeta: ApiRouteMetadata) => {
|
|||
return newMeta;
|
||||
};
|
||||
|
||||
export const apiRoute = (
|
||||
fn: (
|
||||
app: OpenAPIHono /* <{
|
||||
Bindings: {};
|
||||
}> */,
|
||||
) => void,
|
||||
) => fn;
|
||||
export const apiRoute = (fn: (app: OpenAPIHono<HonoEnv>) => void) => fn;
|
||||
|
||||
export const idValidator = createRegExp(
|
||||
anyOf(digit, charIn("ABCDEF")).times(8),
|
||||
|
|
@ -151,12 +144,6 @@ export const handleZodError = (
|
|||
}
|
||||
};
|
||||
|
||||
const getAuth = async (value: Record<string, string>) => {
|
||||
return value.authorization
|
||||
? await getFromHeader(value.authorization)
|
||||
: null;
|
||||
};
|
||||
|
||||
const checkPermissions = (
|
||||
auth: AuthData | null,
|
||||
permissionData: ApiRouteMetadata["permissions"],
|
||||
|
|
@ -300,8 +287,10 @@ export const auth = (
|
|||
permissionData?: ApiRouteMetadata["permissions"],
|
||||
challengeData?: ApiRouteMetadata["challenge"],
|
||||
) =>
|
||||
validator("header", async (value, context) => {
|
||||
const auth = await getAuth(value);
|
||||
createMiddleware<HonoEnv>(async (context, next) => {
|
||||
const header = context.req.header("Authorization");
|
||||
|
||||
const auth = header ? await getFromHeader(header) : null;
|
||||
|
||||
// Only exists for type casting, as otherwise weird errors happen with Hono
|
||||
const fakeResponse = context.json({});
|
||||
|
|
@ -328,13 +317,21 @@ export const auth = (
|
|||
}
|
||||
}
|
||||
|
||||
return checkRouteNeedsAuth(auth, authData, context) as
|
||||
const authCheck = checkRouteNeedsAuth(auth, authData, context) as
|
||||
| typeof fakeResponse
|
||||
| {
|
||||
user: User | null;
|
||||
token: string | null;
|
||||
application: Application | null;
|
||||
};
|
||||
|
||||
if (authCheck instanceof Response) {
|
||||
return authCheck;
|
||||
}
|
||||
|
||||
context.set("auth", authCheck);
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// Helper function to parse form data
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import type { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import type { Config } from "~/packages/config-manager/config.type";
|
||||
import type { HonoEnv } from "~/types/api";
|
||||
|
||||
export const createServer = (config: Config, app: OpenAPIHono) =>
|
||||
export const createServer = (config: Config, app: OpenAPIHono<HonoEnv>) =>
|
||||
Bun.serve({
|
||||
port: config.http.bind_port,
|
||||
reusePort: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue