2024-08-27 17:40:58 +02:00
import { apiRoute , applyConfig , auth , idValidator } from "@/api" ;
import { createRoute } from "@hono/zod-openapi" ;
2024-11-01 20:57:16 +01:00
import { Timeline , User } from "@versia/kit/db" ;
2024-05-06 09:16:33 +02:00
import { and , gt , gte , lt , sql } from "drizzle-orm" ;
import { z } from "zod" ;
2024-06-08 06:57:29 +02:00
import { RolePermissions , Users } from "~/drizzle/schema" ;
2024-08-27 17:40:58 +02:00
import { ErrorSchema } from "~/types/api" ;
2024-05-06 09:16:33 +02:00
export const meta = applyConfig ( {
ratelimits : {
max : 60 ,
duration : 60 ,
} ,
route : "/api/v1/accounts/:id/followers" ,
auth : {
required : false ,
oauthPermissions : [ "read:accounts" ] ,
} ,
2024-06-08 06:57:29 +02:00
permissions : {
required : [
2024-06-13 04:26:43 +02:00
RolePermissions . ViewAccountFollows ,
RolePermissions . ViewAccounts ,
2024-06-08 06:57:29 +02:00
] ,
} ,
2024-05-06 09:16:33 +02:00
} ) ;
export const schemas = {
query : z.object ( {
max_id : z.string ( ) . regex ( idValidator ) . optional ( ) ,
since_id : z.string ( ) . regex ( idValidator ) . optional ( ) ,
min_id : z.string ( ) . regex ( idValidator ) . optional ( ) ,
limit : z.coerce.number ( ) . int ( ) . min ( 1 ) . max ( 40 ) . optional ( ) . default ( 20 ) ,
} ) ,
param : z.object ( {
id : z.string ( ) . uuid ( ) ,
} ) ,
} ;
2024-08-27 17:40:58 +02:00
const route = createRoute ( {
method : "get" ,
path : "/api/v1/accounts/{id}/followers" ,
summary : "Get account followers" ,
description :
"Gets an paginated list of accounts that follow the specified account" ,
middleware : [ auth ( meta . auth , meta . permissions ) ] ,
request : {
params : schemas.param ,
query : schemas.query ,
} ,
responses : {
200 : {
description : "A list of accounts that follow the specified account" ,
content : {
"application/json" : {
schema : z.array ( User . schema ) ,
} ,
} ,
headers : {
Link : {
description : "Links to the next and previous pages" ,
} ,
} ,
} ,
404 : {
description : "The specified account was not found" ,
content : {
"application/json" : {
schema : ErrorSchema ,
} ,
} ,
} ,
} ,
} ) ;
2024-08-19 20:06:38 +02:00
export default apiRoute ( ( app ) = >
2024-08-27 17:40:58 +02:00
app . openapi ( route , async ( context ) = > {
const { id } = context . req . valid ( "param" ) ;
const { max_id , since_id , min_id , limit } = context . req . valid ( "query" ) ;
2024-05-06 09:16:33 +02:00
2024-08-27 17:40:58 +02:00
const otherUser = await User . fromId ( id ) ;
2024-05-06 09:16:33 +02:00
2024-08-27 17:40:58 +02:00
// TODO: Add follower/following privacy settings
2024-05-06 09:16:33 +02:00
2024-08-27 17:40:58 +02:00
if ( ! otherUser ) {
return context . json ( { error : "User not found" } , 404 ) ;
}
2024-05-06 09:16:33 +02:00
2024-08-27 17:40:58 +02:00
const { objects , link } = await Timeline . getUserTimeline (
and (
max_id ? lt ( Users . id , max_id ) : undefined ,
since_id ? gte ( Users . id , since_id ) : undefined ,
min_id ? gt ( Users . id , min_id ) : undefined ,
sql ` EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${ otherUser . id } AND "Relationships"."ownerId" = ${ Users . id } AND "Relationships"."following" = true) ` ,
) ,
limit ,
context . req . url ,
) ;
2024-05-06 09:16:33 +02:00
2024-08-27 17:40:58 +02:00
return context . json (
await Promise . all ( objects . map ( ( object ) = > object . toApi ( ) ) ) ,
200 ,
{
Link : link ,
} ,
) ;
} ) ,
2024-08-19 20:06:38 +02:00
) ;