mirror of
https://iceshrimp.dev/crimekillz/trashposs
synced 2024-11-24 01:39:06 +01:00
[backend/web-api] Add basic timeline endpoint
This commit is contained in:
parent
8134e92284
commit
ba76c5e67b
@ -15,7 +15,7 @@ import { secureRndstr } from "@/misc/secure-rndstr.js";
|
|||||||
@Controller('/auth')
|
@Controller('/auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
@Get('/')
|
@Get('/')
|
||||||
async getAuth(
|
async getAuthStatus(
|
||||||
@CurrentUser() me: ILocalUser | null,
|
@CurrentUser() me: ILocalUser | null,
|
||||||
@CurrentSession() session: Session | null,
|
@CurrentSession() session: Session | null,
|
||||||
): Promise<AuthResponse> {
|
): Promise<AuthResponse> {
|
||||||
@ -30,15 +30,15 @@ export class AuthController {
|
|||||||
@Post('/')
|
@Post('/')
|
||||||
@Flow([RatelimitRouteMiddleware("auth", 10, 60000, true)])
|
@Flow([RatelimitRouteMiddleware("auth", 10, 60000, true)])
|
||||||
async login(@Body({ required: true }) request: AuthRequest): Promise<AuthResponse> {
|
async login(@Body({ required: true }) request: AuthRequest): Promise<AuthResponse> {
|
||||||
if (request.username == null || request.password == null) throw badRequest();
|
if (request.username == null || request.password == null) throw badRequest("Missing username or password");
|
||||||
|
|
||||||
const user = await Users.findOneBy({ usernameLower: request.username.toLowerCase(), host: IsNull() });
|
const user = await Users.findOneBy({ usernameLower: request.username.toLowerCase(), host: IsNull() });
|
||||||
if (!user) throw unauthorized();
|
if (!user) throw unauthorized("Invalid username or password");
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneBy( { userId: user.id });
|
const profile = await UserProfiles.findOneBy( { userId: user.id });
|
||||||
if (!profile || profile.password == null) throw unauthorized();
|
if (!profile || profile.password == null) throw unauthorized("Invalid username or password");
|
||||||
|
|
||||||
if (!await comparePassword(request.password, profile.password)) throw unauthorized();
|
if (!await comparePassword(request.password, profile.password)) throw unauthorized("Invalid username or password");
|
||||||
|
|
||||||
const result = await Sessions.insert({
|
const result = await Sessions.insert({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
@ -50,6 +50,6 @@ export class AuthController {
|
|||||||
|
|
||||||
const session = await Sessions.findOneByOrFail(result.identifiers[0]);
|
const session = await Sessions.findOneByOrFail(result.identifiers[0]);
|
||||||
|
|
||||||
return this.getAuth(user as ILocalUser, session);
|
return this.getAuthStatus(user as ILocalUser, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
packages/backend/src/server/api/web/controllers/timeline.ts
Normal file
20
packages/backend/src/server/api/web/controllers/timeline.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Controller, CurrentUser, Flow, Get, Params, Query } from "@iceshrimp/koa-openapi";
|
||||||
|
import { UserResponse } from "@/server/api/web/entities/user.js";
|
||||||
|
import { TimelineResponse } from "@/server/api/web/entities/note.js";
|
||||||
|
import type { ILocalUser } from "@/models/entities/user.js";
|
||||||
|
import { UserHandler } from "@/server/api/web/handlers/user.js";
|
||||||
|
import { TimelineHandler } from "@/server/api/web/handlers/timeline.js";
|
||||||
|
import { AuthorizationMiddleware } from "@/server/api/web/middleware/auth.js";
|
||||||
|
|
||||||
|
@Controller('/timeline')
|
||||||
|
export class TimelineController {
|
||||||
|
@Get('/home')
|
||||||
|
@Flow([AuthorizationMiddleware()])
|
||||||
|
async getHomeTimeline(
|
||||||
|
@CurrentUser() me: ILocalUser,
|
||||||
|
@Query('limit') limit: number = 20,
|
||||||
|
@Query('replies') replies: boolean = true,
|
||||||
|
): Promise<TimelineResponse> {
|
||||||
|
return TimelineHandler.getHomeTimeline(me, limit, replies);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { ILocalUser } from "@/models/entities/user.js";
|
import { ILocalUser } from "@/models/entities/user.js";
|
||||||
import { NoteResponse } from "@/server/api/web/entities/note.js";
|
import { NoteResponse } from "@/server/api/web/entities/note.js";
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from "@/models/index.js";
|
||||||
import { Boom, notFound, internal } from "@hapi/boom";
|
import { Boom, internal } from "@hapi/boom";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import { UserHandler } from "@/server/api/web/handlers/user.js";
|
import { UserHandler } from "@/server/api/web/handlers/user.js";
|
||||||
import isQuote from "@/misc/is-quote.js";
|
import isQuote from "@/misc/is-quote.js";
|
||||||
|
43
packages/backend/src/server/api/web/handlers/timeline.ts
Normal file
43
packages/backend/src/server/api/web/handlers/timeline.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { TimelineResponse } from "@/server/api/web/entities/note.js";
|
||||||
|
import { UserResponse } from "@/server/api/web/entities/user.js";
|
||||||
|
import { Notes, UserProfiles, Users } from "@/models/index.js";
|
||||||
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
|
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||||
|
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||||
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
|
import { ILocalUser, User } from "@/models/entities/user.js";
|
||||||
|
import { notFound } from "@hapi/boom";
|
||||||
|
import { NoteHandler } from "@/server/api/web/handlers/note.js";
|
||||||
|
import { generateFollowingQuery } from "@/server/api/common/generate-following-query.js";
|
||||||
|
import { generateListQuery } from "@/server/api/common/generate-list-query.js";
|
||||||
|
import { generateChannelQuery } from "@/server/api/common/generate-channel-query.js";
|
||||||
|
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
|
||||||
|
import { generateMutedUserRenotesQueryForNotes } from "@/server/api/common/generated-muted-renote-query.js";
|
||||||
|
|
||||||
|
export class TimelineHandler {
|
||||||
|
public static async getHomeTimeline(me: ILocalUser, limit: number, replies: boolean): Promise<TimelineResponse> {
|
||||||
|
const query = makePaginationQuery(Notes.createQueryBuilder('note'))
|
||||||
|
.innerJoinAndSelect("note.user", "user")
|
||||||
|
.leftJoinAndSelect("note.reply", "reply")
|
||||||
|
.leftJoinAndSelect("note.renote", "renote")
|
||||||
|
.leftJoinAndSelect("reply.user", "replyUser")
|
||||||
|
.leftJoinAndSelect("renote.user", "renoteUser");
|
||||||
|
|
||||||
|
await generateFollowingQuery(query, me);
|
||||||
|
generateListQuery(query, me);
|
||||||
|
generateChannelQuery(query, me);
|
||||||
|
generateRepliesQuery(query, replies, me);
|
||||||
|
generateVisibilityQuery(query, me);
|
||||||
|
generateMutedUserQuery(query, me);
|
||||||
|
generateBlockedUserQuery(query, me);
|
||||||
|
generateMutedUserRenotesQueryForNotes(query, me);
|
||||||
|
|
||||||
|
query.andWhere("note.visibility != 'hidden'");
|
||||||
|
|
||||||
|
const result = query.take(Math.min(limit, 100)).getMany();
|
||||||
|
return {
|
||||||
|
notes: await NoteHandler.encodeMany(await result, me),
|
||||||
|
limit: limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import { ErrorHandlingMiddleware } from "@/server/api/web/middleware/error-handl
|
|||||||
import { AuthController } from "@/server/api/web/controllers/auth.js";
|
import { AuthController } from "@/server/api/web/controllers/auth.js";
|
||||||
import { NoteController } from "@/server/api/web/controllers/note.js";
|
import { NoteController } from "@/server/api/web/controllers/note.js";
|
||||||
import { WebContext, WebRouter } from "@/server/api/web/misc/koa.js";
|
import { WebContext, WebRouter } from "@/server/api/web/misc/koa.js";
|
||||||
|
import { TimelineController } from "@/server/api/web/controllers/timeline.js";
|
||||||
|
|
||||||
export class WebAPI {
|
export class WebAPI {
|
||||||
private readonly router: WebRouter;
|
private readonly router: WebRouter;
|
||||||
@ -26,6 +27,7 @@ export class WebAPI {
|
|||||||
UserController,
|
UserController,
|
||||||
NoteController,
|
NoteController,
|
||||||
AuthController,
|
AuthController,
|
||||||
|
TimelineController,
|
||||||
],
|
],
|
||||||
flow: [
|
flow: [
|
||||||
AuthenticationMiddleware,
|
AuthenticationMiddleware,
|
||||||
|
@ -3,6 +3,7 @@ import { Next } from "koa";
|
|||||||
import { Sessions } from "@/models/index.js";
|
import { Sessions } from "@/models/index.js";
|
||||||
import { Session } from "@/models/entities/session.js";
|
import { Session } from "@/models/entities/session.js";
|
||||||
import { ILocalUser } from "@/models/entities/user.js";
|
import { ILocalUser } from "@/models/entities/user.js";
|
||||||
|
import { unauthorized } from "@hapi/boom";
|
||||||
|
|
||||||
export const AuthenticationMiddleware: WebMiddleware = async (ctx: WebContext, next: Next) => {
|
export const AuthenticationMiddleware: WebMiddleware = async (ctx: WebContext, next: Next) => {
|
||||||
const session = await authenticate(ctx.headers.authorization);
|
const session = await authenticate(ctx.headers.authorization);
|
||||||
@ -12,13 +13,11 @@ export const AuthenticationMiddleware: WebMiddleware = async (ctx: WebContext, n
|
|||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AuthorizationMiddleware(required: boolean, scopes: string[] = []): WebMiddleware {
|
export function AuthorizationMiddleware(admin: boolean = false): WebMiddleware {
|
||||||
return async (ctx: WebContext, next: Next) => {
|
return async (ctx: WebContext, next: Next) => {
|
||||||
try {
|
if (!ctx.state.session?.active || (admin && !ctx.state.session?.user.isAdmin)) {
|
||||||
if (required && !ctx.state.session?.active) {
|
throw unauthorized("This method requires an authenticated user");
|
||||||
throw new Error(); //FIXME
|
}
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user