2024-04-14 13:22:47 +02:00
import { dualLogger } from "@loggers" ;
2024-04-07 07:30:49 +02:00
import { addUserToMeilisearch } from "@meilisearch" ;
2024-04-25 05:40:27 +02:00
import { config } from "config-manager" ;
import { type InferSelectModel , and , eq , inArray , sql } from "drizzle-orm" ;
2024-04-10 01:54:10 +02:00
import type * as Lysand from "lysand-types" ;
2024-04-13 14:20:12 +02:00
import { db } from "~drizzle/db" ;
2024-04-11 13:39:07 +02:00
import {
2024-04-17 08:36:01 +02:00
Applications ,
Instances ,
Notifications ,
Relationships ,
Tokens ,
Users ,
2024-04-11 13:39:07 +02:00
} from "~drizzle/schema" ;
2024-04-25 05:40:27 +02:00
import { User } from "~packages/database-interface/user" ;
2024-04-14 13:20:55 +02:00
import { LogLevel } from "~packages/log-manager" ;
2024-04-16 08:00:40 +02:00
import type { Application } from "./Application" ;
2024-04-25 05:40:27 +02:00
import type { EmojiWithInstance } from "./Emoji" ;
2024-04-13 14:20:12 +02:00
import { objectToInboxRequest } from "./Federation" ;
import { createNewRelationship } from "./Relationship" ;
2024-04-16 04:09:16 +02:00
import type { Token } from "./Token" ;
2024-04-11 13:39:07 +02:00
2024-04-25 05:40:27 +02:00
export type UserType = InferSelectModel < typeof Users > ;
2024-04-17 06:09:21 +02:00
2024-04-25 05:40:27 +02:00
export type UserWithInstance = UserType & {
2024-04-17 08:36:01 +02:00
instance : InferSelectModel < typeof Instances > | null ;
2024-04-11 13:39:07 +02:00
} ;
2024-03-11 03:04:14 +01:00
2024-04-25 05:40:27 +02:00
export type UserWithRelations = UserType & {
2024-04-17 08:36:01 +02:00
instance : InferSelectModel < typeof Instances > | null ;
2024-04-11 13:39:07 +02:00
emojis : EmojiWithInstance [ ] ;
followerCount : number ;
followingCount : number ;
statusCount : number ;
} ;
2023-10-23 07:39:42 +02:00
2024-04-11 13:39:07 +02:00
export type UserWithRelationsAndRelationships = UserWithRelations & {
2024-04-17 08:36:01 +02:00
relationships : InferSelectModel < typeof Relationships > [ ] ;
relationshipSubjects : InferSelectModel < typeof Relationships > [ ] ;
2024-04-11 13:39:07 +02:00
} ;
export const userRelations : {
instance : true ;
emojis : {
with : {
emoji : {
with : {
instance : true ;
} ;
} ;
} ;
} ;
} = {
instance : true ,
emojis : {
with : {
emoji : {
with : {
instance : true ,
} ,
} ,
} ,
} ,
} ;
export const userExtras = {
followerCount :
2024-04-17 08:36:01 +02:00
sql ` (SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "Users".id AND "relationships"."following" = true)) ` . as (
2024-04-11 13:39:07 +02:00
"follower_count" ,
) ,
followingCount :
2024-04-17 08:36:01 +02:00
sql ` (SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "Users".id AND "relationshipSubjects"."following" = true)) ` . as (
2024-04-11 13:39:07 +02:00
"following_count" ,
) ,
statusCount :
2024-04-17 08:36:01 +02:00
sql ` (SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "Users".id) ` . as (
2024-04-11 13:39:07 +02:00
"status_count" ,
) ,
} ;
2023-11-04 04:34:31 +01:00
2024-04-11 13:39:07 +02:00
export const userExtrasTemplate = ( name : string ) = > ( {
// @ts-ignore
followerCount : sql ( [
2024-04-17 08:36:01 +02:00
` (SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = " ${ name } ".id AND "relationships"."following" = true)) ` ,
2024-04-11 13:39:07 +02:00
] ) . as ( "follower_count" ) ,
// @ts-ignore
followingCount : sql ( [
2024-04-17 08:36:01 +02:00
` (SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = " ${ name } ".id AND "relationshipSubjects"."following" = true)) ` ,
2024-04-11 13:39:07 +02:00
] ) . as ( "following_count" ) ,
// @ts-ignore
statusCount : sql ( [
2024-04-17 08:36:01 +02:00
` (SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = " ${ name } ".id) ` ,
2024-04-11 13:39:07 +02:00
] ) . as ( "status_count" ) ,
2023-11-27 01:56:16 +01:00
} ) ;
2024-04-11 13:39:07 +02:00
export interface AuthData {
2024-04-25 05:40:27 +02:00
user : User | null ;
2024-04-11 13:39:07 +02:00
token : string ;
2024-04-16 04:09:16 +02:00
application : Application | null ;
2024-04-11 13:39:07 +02:00
}
2023-10-08 22:20:42 +02:00
2023-11-11 03:36:06 +01:00
export const getFromRequest = async ( req : Request ) : Promise < AuthData > = > {
2024-04-07 07:30:49 +02:00
// Check auth token
const token = req . headers . get ( "Authorization" ) ? . split ( " " ) [ 1 ] || "" ;
2023-11-11 03:36:06 +01:00
2024-04-16 04:09:16 +02:00
const { user , application } =
await retrieveUserAndApplicationFromToken ( token ) ;
return { user , token , application } ;
2023-11-11 03:36:06 +01:00
} ;
2024-04-10 07:51:00 +02:00
export const followRequestUser = async (
2024-04-09 04:12:54 +02:00
follower : User ,
followee : User ,
2024-04-09 04:26:48 +02:00
relationshipId : string ,
2024-04-09 04:12:54 +02:00
reblogs = false ,
notify = false ,
languages : string [ ] = [ ] ,
2024-04-17 08:36:01 +02:00
) : Promise < InferSelectModel < typeof Relationships > > = > {
2024-04-25 05:40:27 +02:00
const isRemote = followee . isRemote ( ) ;
2024-04-10 07:51:00 +02:00
2024-04-11 13:39:07 +02:00
const updatedRelationship = (
await db
2024-04-17 08:36:01 +02:00
. update ( Relationships )
2024-04-11 13:39:07 +02:00
. set ( {
2024-04-25 05:40:27 +02:00
following : isRemote ? false : ! followee . getUser ( ) . isLocked ,
requested : isRemote ? true : followee . getUser ( ) . isLocked ,
2024-04-11 13:39:07 +02:00
showingReblogs : reblogs ,
notifying : notify ,
languages : languages ,
} )
2024-04-17 08:36:01 +02:00
. where ( eq ( Relationships . id , relationshipId ) )
2024-04-11 13:39:07 +02:00
. returning ( )
) [ 0 ] ;
2024-04-10 09:54:15 +02:00
2024-04-10 07:51:00 +02:00
if ( isRemote ) {
// Federate
// TODO: Make database job
const request = await objectToInboxRequest (
followRequestToLysand ( follower , followee ) ,
follower ,
followee ,
) ;
2024-04-09 04:12:54 +02:00
2024-04-10 07:51:00 +02:00
// Send request
const response = await fetch ( request ) ;
2024-04-09 04:12:54 +02:00
2024-04-10 07:51:00 +02:00
if ( ! response . ok ) {
2024-04-14 13:20:55 +02:00
dualLogger . log (
LogLevel . DEBUG ,
"Federation.FollowRequest" ,
await response . text ( ) ,
) ;
dualLogger . log (
LogLevel . ERROR ,
"Federation.FollowRequest" ,
2024-04-25 05:40:27 +02:00
` Failed to federate follow request from ${
follower . id
} to $ { followee . getUri ( ) } ` ,
2024-04-10 07:51:00 +02:00
) ;
2024-04-10 09:54:15 +02:00
2024-04-11 13:39:07 +02:00
return (
await db
2024-04-17 08:36:01 +02:00
. update ( Relationships )
2024-04-11 13:39:07 +02:00
. set ( {
following : false ,
requested : false ,
} )
2024-04-17 08:36:01 +02:00
. where ( eq ( Relationships . id , relationshipId ) )
2024-04-11 13:39:07 +02:00
. returning ( )
) [ 0 ] ;
2024-04-10 07:51:00 +02:00
}
2024-04-09 04:12:54 +02:00
} else {
2024-04-17 08:36:01 +02:00
await db . insert ( Notifications ) . values ( {
2024-04-16 07:40:35 +02:00
accountId : follower.id ,
2024-04-25 05:40:27 +02:00
type : followee . getUser ( ) . isLocked ? "follow_request" : "follow" ,
2024-04-16 07:40:35 +02:00
notifiedId : followee.id ,
2024-04-11 13:39:07 +02:00
} ) ;
2024-04-09 04:12:54 +02:00
}
2024-04-11 13:39:07 +02:00
return updatedRelationship ;
2024-04-09 04:12:54 +02:00
} ;
2024-04-10 10:07:03 +02:00
export const sendFollowAccept = async ( follower : User , followee : User ) = > {
// TODO: Make database job
const request = await objectToInboxRequest (
followAcceptToLysand ( follower , followee ) ,
followee ,
follower ,
) ;
// Send request
const response = await fetch ( request ) ;
if ( ! response . ok ) {
2024-04-14 13:20:55 +02:00
dualLogger . log (
LogLevel . DEBUG ,
"Federation.FollowAccept" ,
await response . text ( ) ,
) ;
dualLogger . log (
LogLevel . ERROR ,
"Federation.FollowAccept" ,
2024-04-25 05:40:27 +02:00
` Failed to federate follow accept from ${
followee . id
} to $ { follower . getUri ( ) } ` ,
2024-04-10 10:07:03 +02:00
) ;
}
} ;
export const sendFollowReject = async ( follower : User , followee : User ) = > {
// TODO: Make database job
const request = await objectToInboxRequest (
followRejectToLysand ( follower , followee ) ,
followee ,
follower ,
) ;
// Send request
const response = await fetch ( request ) ;
if ( ! response . ok ) {
2024-04-14 13:20:55 +02:00
dualLogger . log (
LogLevel . DEBUG ,
"Federation.FollowReject" ,
await response . text ( ) ,
) ;
dualLogger . log (
LogLevel . ERROR ,
"Federation.FollowReject" ,
2024-04-25 05:40:27 +02:00
` Failed to federate follow reject from ${
followee . id
} to $ { follower . getUri ( ) } ` ,
2024-04-10 10:07:03 +02:00
) ;
}
} ;
2024-04-11 13:39:07 +02:00
export const transformOutputToUserWithRelations = (
2024-04-25 05:40:27 +02:00
user : Omit < UserType , " endpoints " > & {
2024-04-11 13:39:07 +02:00
followerCount : unknown ;
followingCount : unknown ;
statusCount : unknown ;
emojis : {
2024-04-14 03:21:38 +02:00
userId : string ;
emojiId : string ;
2024-04-11 13:39:07 +02:00
emoji? : EmojiWithInstance ;
} [ ] ;
2024-04-17 08:36:01 +02:00
instance : InferSelectModel < typeof Instances > | null ;
2024-04-11 13:39:07 +02:00
endpoints : unknown ;
} ,
) : UserWithRelations = > {
return {
. . . user ,
followerCount : Number ( user . followerCount ) ,
followingCount : Number ( user . followingCount ) ,
statusCount : Number ( user . statusCount ) ,
endpoints :
user . endpoints ? ?
( { } as Partial < {
dislikes : string ;
featured : string ;
likes : string ;
followers : string ;
following : string ;
inbox : string ;
outbox : string ;
} > ) ,
emojis : user.emojis.map (
( emoji ) = >
( emoji as unknown as Record < string , object > )
. emoji as EmojiWithInstance ,
) ,
} ;
} ;
export const findManyUsers = async (
2024-04-17 08:36:01 +02:00
query : Parameters < typeof db.query.Users.findMany > [ 0 ] ,
2024-04-11 13:39:07 +02:00
) : Promise < UserWithRelations [ ] > = > {
2024-04-17 08:36:01 +02:00
const output = await db . query . Users . findMany ( {
2024-04-11 13:39:07 +02:00
. . . query ,
with : {
. . . userRelations ,
. . . query ? . with ,
} ,
extras : {
. . . userExtras ,
. . . query ? . extras ,
} ,
} ) ;
return output . map ( ( user ) = > transformOutputToUserWithRelations ( user ) ) ;
} ;
export const findFirstUser = async (
2024-04-17 08:36:01 +02:00
query : Parameters < typeof db.query.Users.findFirst > [ 0 ] ,
2024-04-11 13:39:07 +02:00
) : Promise < UserWithRelations | null > = > {
2024-04-17 08:36:01 +02:00
const output = await db . query . Users . findFirst ( {
2024-04-11 13:39:07 +02:00
. . . query ,
with : {
. . . userRelations ,
. . . query ? . with ,
2024-04-07 07:30:49 +02:00
} ,
2024-04-11 13:39:07 +02:00
extras : {
. . . userExtras ,
. . . query ? . extras ,
} ,
} ) ;
if ( ! output ) return null ;
return transformOutputToUserWithRelations ( output ) ;
} ;
2024-04-10 06:22:57 +02:00
/ * *
* Resolves a WebFinger identifier to a user .
* @param identifier Either a UUID or a username
* /
2024-04-11 13:39:07 +02:00
export const resolveWebFinger = async (
identifier : string ,
host : string ,
2024-04-25 05:40:27 +02:00
) : Promise < User | null > = > {
2024-04-10 06:22:57 +02:00
// Check if user not already in database
2024-04-11 13:39:07 +02:00
const foundUser = await db
. select ( )
2024-04-17 08:36:01 +02:00
. from ( Users )
. innerJoin ( Instances , eq ( Users . instanceId , Instances . id ) )
. where ( and ( eq ( Users . username , identifier ) , eq ( Instances . baseUrl , host ) ) )
2024-04-11 13:39:07 +02:00
. limit ( 1 ) ;
2024-04-25 05:40:27 +02:00
if ( foundUser [ 0 ] ) return await User . fromId ( foundUser [ 0 ] . Users . id ) ;
2024-04-10 06:22:57 +02:00
const hostWithProtocol = host . startsWith ( "http" ) ? host : ` https:// ${ host } ` ;
const response = await fetch (
new URL (
` /.well-known/webfinger? ${ new URLSearchParams ( {
resource : ` acct: ${ identifier } @ ${ host } ` ,
} ) } ` ,
hostWithProtocol ,
) ,
{
method : "GET" ,
headers : {
Accept : "application/json" ,
} ,
} ,
) ;
2024-04-10 08:27:16 +02:00
if ( response . status === 404 ) {
return null ;
}
2024-04-10 06:22:57 +02:00
const data = ( await response . json ( ) ) as {
subject : string ;
links : {
rel : string ;
type : string ;
href : string ;
} [ ] ;
} ;
if ( ! data . subject || ! data . links ) {
throw new Error (
"Invalid WebFinger data (missing subject or links from response)" ,
) ;
}
const relevantLink = data . links . find ( ( link ) = > link . rel === "self" ) ;
if ( ! relevantLink ) {
throw new Error (
"Invalid WebFinger data (missing link with rel: 'self')" ,
) ;
}
2024-04-25 05:40:27 +02:00
return User . resolve ( relevantLink . href ) ;
2024-04-10 06:22:57 +02:00
} ;
2023-11-11 03:36:06 +01:00
/ * *
* Fetches the list of followers associated with the actor and updates the user ' s followers
* /
export const fetchFollowers = ( ) = > {
2024-04-07 07:30:49 +02:00
//
2023-11-11 03:36:06 +01:00
} ;
2023-09-22 05:18:05 +02:00
2023-11-11 03:36:06 +01:00
/ * *
* Creates a new LOCAL user .
* @param data The data for the new user .
* @returns The newly created user .
* /
export const createNewLocalUser = async ( data : {
2024-04-07 07:30:49 +02:00
username : string ;
display_name? : string ;
password : string ;
email : string ;
bio? : string ;
avatar? : string ;
header? : string ;
admin? : boolean ;
2024-04-11 15:52:44 +02:00
skipPasswordHash? : boolean ;
2024-04-25 05:40:27 +02:00
} ) : Promise < User | null > = > {
2024-04-07 07:30:49 +02:00
const keys = await generateUserKeys ( ) ;
2024-04-11 13:39:07 +02:00
const newUser = (
await db
2024-04-17 08:36:01 +02:00
. insert ( Users )
2024-04-11 13:39:07 +02:00
. values ( {
username : data.username ,
displayName : data.display_name ? ? data . username ,
2024-04-11 15:52:44 +02:00
password : data.skipPasswordHash
? data . password
: await Bun . password . hash ( data . password ) ,
2024-04-11 13:39:07 +02:00
email : data.email ,
note : data.bio ? ? "" ,
avatar : data.avatar ? ? config . defaults . avatar ,
header : data.header ? ? config . defaults . avatar ,
isAdmin : data.admin ? ? false ,
publicKey : keys.public_key ,
privateKey : keys.private_key ,
updatedAt : new Date ( ) . toISOString ( ) ,
source : {
language : null ,
note : "" ,
privacy : "public" ,
sensitive : false ,
fields : [ ] ,
} ,
} )
. returning ( )
) [ 0 ] ;
2024-04-25 05:40:27 +02:00
const finalUser = await User . fromId ( newUser . id ) ;
2024-04-07 07:30:49 +02:00
2024-04-11 13:39:07 +02:00
if ( ! finalUser ) return null ;
2024-04-07 07:30:49 +02:00
// Add to Meilisearch
2024-04-11 13:39:07 +02:00
await addUserToMeilisearch ( finalUser ) ;
2024-04-07 07:30:49 +02:00
2024-04-11 13:39:07 +02:00
return finalUser ;
2023-11-11 03:36:06 +01:00
} ;
2023-09-22 03:09:14 +02:00
2023-11-11 03:36:06 +01:00
/ * *
* Parses mentions from a list of URIs
* /
2024-04-11 13:39:07 +02:00
export const parseMentionsUris = async (
mentions : string [ ] ,
2024-04-25 05:40:27 +02:00
) : Promise < User [ ] > = > {
return await User . manyFromSql ( inArray ( Users . uri , mentions ) ) ;
2023-11-11 03:36:06 +01:00
} ;
2023-09-22 03:09:14 +02:00
2023-11-11 03:36:06 +01:00
/ * *
* Retrieves a user from a token .
* @param access_token The access token to retrieve the user from .
* @returns The user associated with the given access token .
* /
2024-04-11 13:39:07 +02:00
export const retrieveUserFromToken = async (
access_token : string ,
2024-04-25 05:40:27 +02:00
) : Promise < User | null > = > {
2024-04-07 07:30:49 +02:00
if ( ! access_token ) return null ;
2024-04-16 04:09:16 +02:00
const token = await retrieveToken ( access_token ) ;
2024-04-07 07:30:49 +02:00
2024-04-11 13:39:07 +02:00
if ( ! token || ! token . userId ) return null ;
2024-04-25 05:40:27 +02:00
const user = await User . fromId ( token . userId ) ;
2024-04-07 07:30:49 +02:00
2024-04-11 13:39:07 +02:00
return user ;
2023-11-11 03:36:06 +01:00
} ;
2023-09-22 05:18:05 +02:00
2024-04-16 04:09:16 +02:00
export const retrieveUserAndApplicationFromToken = async (
access_token : string ,
) : Promise < {
2024-04-25 05:40:27 +02:00
user : User | null ;
2024-04-16 04:09:16 +02:00
application : Application | null ;
} > = > {
if ( ! access_token ) return { user : null , application : null } ;
const output = (
await db
. select ( {
2024-04-17 08:36:01 +02:00
token : Tokens ,
application : Applications ,
2024-04-16 04:09:16 +02:00
} )
2024-04-17 08:36:01 +02:00
. from ( Tokens )
. leftJoin ( Applications , eq ( Tokens . applicationId , Applications . id ) )
. where ( eq ( Tokens . accessToken , access_token ) )
2024-04-16 04:09:16 +02:00
. limit ( 1 )
) [ 0 ] ;
if ( ! output ? . token . userId ) return { user : null , application : null } ;
2024-04-25 05:40:27 +02:00
const user = await User . fromId ( output . token . userId ) ;
2024-04-16 04:09:16 +02:00
return { user , application : output.application ? ? null } ;
} ;
export const retrieveToken = async (
access_token : string ,
) : Promise < Token | null > = > {
if ( ! access_token ) return null ;
return (
2024-04-17 08:36:01 +02:00
( await db . query . Tokens . findFirst ( {
2024-04-16 04:09:16 +02:00
where : ( tokens , { eq } ) = > eq ( tokens . accessToken , access_token ) ,
} ) ) ? ? null
) ;
} ;
2023-11-11 03:36:06 +01:00
/ * *
* Gets the relationship to another user .
* @param other The other user to get the relationship to .
* @returns The relationship to the other user .
* /
export const getRelationshipToOtherUser = async (
2024-04-25 05:40:27 +02:00
user : User ,
2024-04-07 07:30:49 +02:00
other : User ,
2024-04-17 08:36:01 +02:00
) : Promise < InferSelectModel < typeof Relationships > > = > {
const foundRelationship = await db . query . Relationships . findFirst ( {
2024-04-11 13:39:07 +02:00
where : ( relationship , { and , eq } ) = >
and (
eq ( relationship . ownerId , user . id ) ,
eq ( relationship . subjectId , other . id ) ,
) ,
2024-04-07 07:30:49 +02:00
} ) ;
2024-04-09 04:12:54 +02:00
2024-04-11 13:39:07 +02:00
if ( ! foundRelationship ) {
2024-04-09 04:12:54 +02:00
// Create new relationship
const newRelationship = await createNewRelationship ( user , other ) ;
return newRelationship ;
}
2024-04-11 13:39:07 +02:00
return foundRelationship ;
2023-11-11 03:36:06 +01:00
} ;
2023-09-22 03:09:14 +02:00
2023-11-11 03:36:06 +01:00
/ * *
* Generates keys for the user .
* /
export const generateUserKeys = async ( ) = > {
2024-04-07 07:30:49 +02:00
const keys = await crypto . subtle . generateKey ( "Ed25519" , true , [
"sign" ,
"verify" ,
] ) ;
2024-04-18 10:42:12 +02:00
const privateKey = Buffer . from (
await crypto . subtle . exportKey ( "pkcs8" , keys . privateKey ) ,
) . toString ( "base64" ) ;
const publicKey = Buffer . from (
await crypto . subtle . exportKey ( "spki" , keys . publicKey ) ,
) . toString ( "base64" ) ;
2024-04-07 07:30:49 +02:00
// Add header, footer and newlines later on
// These keys are base64 encrypted
return {
private_key : privateKey ,
public_key : publicKey ,
} ;
2023-11-11 03:36:06 +01:00
} ;
2024-04-10 07:51:00 +02:00
export const followRequestToLysand = (
follower : User ,
followee : User ,
) : Lysand . Follow = > {
2024-04-25 05:40:27 +02:00
if ( follower . isRemote ( ) ) {
2024-04-10 07:51:00 +02:00
throw new Error ( "Follower must be a local user" ) ;
}
2024-04-25 05:40:27 +02:00
if ( ! followee . isRemote ( ) ) {
2024-04-10 07:51:00 +02:00
throw new Error ( "Followee must be a remote user" ) ;
}
2024-04-25 05:40:27 +02:00
if ( ! followee . getUser ( ) . uri ) {
2024-04-10 07:51:00 +02:00
throw new Error ( "Followee must have a URI in database" ) ;
}
const id = crypto . randomUUID ( ) ;
return {
type : "Follow" ,
id : id ,
2024-04-25 05:40:27 +02:00
author : follower.getUri ( ) ,
followee : followee.getUri ( ) ,
2024-04-10 07:51:00 +02:00
created_at : new Date ( ) . toISOString ( ) ,
uri : new URL ( ` /follows/ ${ id } ` , config . http . base_url ) . toString ( ) ,
} ;
} ;
export const followAcceptToLysand = (
follower : User ,
followee : User ,
) : Lysand . FollowAccept = > {
2024-04-25 05:40:27 +02:00
if ( ! follower . isRemote ( ) ) {
2024-04-10 09:18:41 +02:00
throw new Error ( "Follower must be a remote user" ) ;
2024-04-10 07:51:00 +02:00
}
2024-04-25 05:40:27 +02:00
if ( followee . isRemote ( ) ) {
2024-04-10 09:18:41 +02:00
throw new Error ( "Followee must be a local user" ) ;
2024-04-10 07:51:00 +02:00
}
2024-04-25 05:40:27 +02:00
if ( ! follower . getUser ( ) . uri ) {
2024-04-10 09:18:41 +02:00
throw new Error ( "Follower must have a URI in database" ) ;
2024-04-10 07:51:00 +02:00
}
const id = crypto . randomUUID ( ) ;
return {
type : "FollowAccept" ,
id : id ,
2024-04-25 05:40:27 +02:00
author : followee.getUri ( ) ,
2024-04-10 07:51:00 +02:00
created_at : new Date ( ) . toISOString ( ) ,
2024-04-25 05:40:27 +02:00
follower : follower.getUri ( ) ,
2024-04-10 07:51:00 +02:00
uri : new URL ( ` /follows/ ${ id } ` , config . http . base_url ) . toString ( ) ,
} ;
} ;
2024-04-10 10:07:03 +02:00
export const followRejectToLysand = (
follower : User ,
followee : User ,
) : Lysand . FollowReject = > {
return {
. . . followAcceptToLysand ( follower , followee ) ,
type : "FollowReject" ,
} ;
} ;