mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
Add more API endpoints, better tests
This commit is contained in:
parent
dacbc5a00b
commit
6d2f9072ac
15 changed files with 628 additions and 153 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { APIApplication } from "~types/entities/application";
|
||||
import { Token } from "./Token";
|
||||
|
||||
/**
|
||||
* Applications from clients
|
||||
|
|
@ -36,6 +37,16 @@ export class Application extends BaseEntity {
|
|||
@Column("varchar")
|
||||
redirect_uris = "urn:ietf:wg:oauth:2.0:oob";
|
||||
|
||||
static async getFromToken(token: string) {
|
||||
const dbToken = await Token.findOne({
|
||||
where: {
|
||||
access_token: token,
|
||||
},
|
||||
});
|
||||
|
||||
return dbToken?.application || null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async toAPI(): Promise<APIApplication> {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ import { RawActor } from "./RawActor";
|
|||
import { getConfig } from "@config";
|
||||
import { errorResponse } from "@response";
|
||||
|
||||
/**
|
||||
* Stores an ActivityPub activity as raw JSON-LD data
|
||||
*/
|
||||
@Entity({
|
||||
name: "activities",
|
||||
})
|
||||
|
|
@ -25,25 +22,19 @@ export class RawActivity extends BaseEntity {
|
|||
@Column("jsonb")
|
||||
data!: APActivity;
|
||||
|
||||
// Any associated objects (there is typically only one)
|
||||
@ManyToMany(() => RawObject, object => object.id)
|
||||
@ManyToMany(() => RawObject)
|
||||
@JoinTable()
|
||||
objects!: RawObject[];
|
||||
|
||||
@ManyToMany(() => RawActor, actor => actor.id)
|
||||
@ManyToMany(() => RawActor)
|
||||
@JoinTable()
|
||||
actors!: RawActor[];
|
||||
|
||||
static async getByObjectId(id: string) {
|
||||
return await RawActivity.createQueryBuilder("activity")
|
||||
// Objects is a many-to-many relationship
|
||||
.leftJoinAndSelect("activity.objects", "objects")
|
||||
.leftJoinAndSelect("activity.actors", "actors")
|
||||
.where("objects.data @> :data", {
|
||||
data: JSON.stringify({
|
||||
id,
|
||||
}),
|
||||
})
|
||||
.where("objects.data @> :data", { data: JSON.stringify({ id }) })
|
||||
.getMany();
|
||||
}
|
||||
|
||||
|
|
@ -51,21 +42,15 @@ export class RawActivity extends BaseEntity {
|
|||
return await RawActivity.createQueryBuilder("activity")
|
||||
.leftJoinAndSelect("activity.objects", "objects")
|
||||
.leftJoinAndSelect("activity.actors", "actors")
|
||||
.where("activity.data->>'id' = :id", {
|
||||
id,
|
||||
})
|
||||
.where("activity.data->>'id' = :id", { id })
|
||||
.getOne();
|
||||
}
|
||||
|
||||
static async getLatestById(id: string) {
|
||||
return await RawActivity.createQueryBuilder("activity")
|
||||
// Where id is part of the jsonb column 'data'
|
||||
.where("activity.data->>'id' = :id", {
|
||||
id,
|
||||
})
|
||||
.where("activity.data->>'id' = :id", { id })
|
||||
.leftJoinAndSelect("activity.objects", "objects")
|
||||
.leftJoinAndSelect("activity.actors", "actors")
|
||||
// Sort by most recent
|
||||
.orderBy("activity.data->>'published'", "DESC")
|
||||
.getOne();
|
||||
}
|
||||
|
|
@ -77,24 +62,26 @@ export class RawActivity extends BaseEntity {
|
|||
static async updateObjectIfExists(object: APObject) {
|
||||
const rawObject = await RawObject.getById(object.id ?? "");
|
||||
|
||||
if (rawObject) {
|
||||
rawObject.data = object;
|
||||
|
||||
// Check if object body contains any filtered terms
|
||||
if (await rawObject.isObjectFiltered())
|
||||
return errorResponse("Object filtered", 409);
|
||||
|
||||
await rawObject.save();
|
||||
return rawObject;
|
||||
} else {
|
||||
if (!rawObject) {
|
||||
return errorResponse("Object does not exist", 404);
|
||||
}
|
||||
|
||||
rawObject.data = object;
|
||||
|
||||
if (await rawObject.isObjectFiltered()) {
|
||||
return errorResponse("Object filtered", 409);
|
||||
}
|
||||
|
||||
await rawObject.save();
|
||||
return rawObject;
|
||||
}
|
||||
|
||||
static async deleteObjectIfExists(object: APObject) {
|
||||
const dbObject = await RawObject.getById(object.id ?? "");
|
||||
|
||||
if (!dbObject) return errorResponse("Object does not exist", 404);
|
||||
if (!dbObject) {
|
||||
return errorResponse("Object does not exist", 404);
|
||||
}
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
@ -110,16 +97,12 @@ export class RawActivity extends BaseEntity {
|
|||
} else {
|
||||
const activities = await RawActivity.getByObjectId(object.id ?? "");
|
||||
|
||||
activities.forEach(
|
||||
activity =>
|
||||
(activity.objects = activity.objects.filter(
|
||||
o => o.id !== object.id
|
||||
))
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
activities.map(async activity => await activity.save())
|
||||
);
|
||||
for (const activity of activities) {
|
||||
activity.objects = activity.objects.filter(
|
||||
o => o.id !== object.id
|
||||
);
|
||||
await activity.save();
|
||||
}
|
||||
|
||||
await dbObject.remove();
|
||||
}
|
||||
|
|
@ -127,23 +110,27 @@ export class RawActivity extends BaseEntity {
|
|||
return dbObject;
|
||||
}
|
||||
|
||||
static async addIfNotExists(activity: APActivity) {
|
||||
if (!(await RawActivity.exists(activity.id ?? ""))) {
|
||||
const rawActivity = new RawActivity();
|
||||
rawActivity.data = {
|
||||
...activity,
|
||||
object: undefined,
|
||||
actor: undefined,
|
||||
};
|
||||
static async addIfNotExists(activity: APActivity, addObject?: RawObject) {
|
||||
if (await RawActivity.exists(activity.id ?? "")) {
|
||||
return errorResponse("Activity already exists", 409);
|
||||
}
|
||||
|
||||
const actor = await rawActivity.addActorIfNotExists(
|
||||
activity.actor as APActor
|
||||
);
|
||||
const rawActivity = new RawActivity();
|
||||
rawActivity.data = { ...activity, object: undefined, actor: undefined };
|
||||
rawActivity.actors = [];
|
||||
rawActivity.objects = [];
|
||||
|
||||
if (actor instanceof Response) {
|
||||
return actor;
|
||||
}
|
||||
const actor = await rawActivity.addActorIfNotExists(
|
||||
activity.actor as APActor
|
||||
);
|
||||
|
||||
if (actor instanceof Response) {
|
||||
return actor;
|
||||
}
|
||||
|
||||
if (addObject) {
|
||||
rawActivity.objects.push(addObject);
|
||||
} else {
|
||||
const object = await rawActivity.addObjectIfNotExists(
|
||||
activity.object as APObject
|
||||
);
|
||||
|
|
@ -151,67 +138,77 @@ export class RawActivity extends BaseEntity {
|
|||
if (object instanceof Response) {
|
||||
return object;
|
||||
}
|
||||
|
||||
await rawActivity.save();
|
||||
return rawActivity;
|
||||
} else {
|
||||
return errorResponse("Activity already exists", 409);
|
||||
}
|
||||
|
||||
await rawActivity.save();
|
||||
return rawActivity;
|
||||
}
|
||||
|
||||
makeActivityPubRepresentation() {
|
||||
return {
|
||||
...this.data,
|
||||
object: this.objects[0].data,
|
||||
actor: this.actors[0].data,
|
||||
};
|
||||
}
|
||||
|
||||
async addObjectIfNotExists(object: APObject) {
|
||||
if (!this.objects.some(o => o.data.id === object.id)) {
|
||||
const rawObject = new RawObject();
|
||||
rawObject.data = object;
|
||||
|
||||
// Check if object body contains any filtered terms
|
||||
if (await rawObject.isObjectFiltered())
|
||||
return errorResponse("Object filtered", 409);
|
||||
|
||||
await rawObject.save();
|
||||
|
||||
this.objects.push(rawObject);
|
||||
|
||||
return rawObject;
|
||||
} else {
|
||||
if (this.objects.some(o => o.data.id === object.id)) {
|
||||
return errorResponse("Object already exists", 409);
|
||||
}
|
||||
|
||||
const rawObject = new RawObject();
|
||||
rawObject.data = object;
|
||||
|
||||
if (await rawObject.isObjectFiltered()) {
|
||||
return errorResponse("Object filtered", 409);
|
||||
}
|
||||
|
||||
await rawObject.save();
|
||||
this.objects.push(rawObject);
|
||||
return rawObject;
|
||||
}
|
||||
|
||||
async addActorIfNotExists(actor: APActor) {
|
||||
if (!this.actors.some(a => a.data.id === actor.id)) {
|
||||
const rawActor = new RawActor();
|
||||
rawActor.data = actor;
|
||||
const dbActor = await RawActor.getByActorId(actor.id ?? "");
|
||||
|
||||
const config = getConfig();
|
||||
if (dbActor) {
|
||||
this.actors.push(dbActor);
|
||||
return dbActor;
|
||||
}
|
||||
|
||||
if (
|
||||
config.activitypub.discard_avatars.find(
|
||||
instance => actor.id?.includes(instance)
|
||||
)
|
||||
) {
|
||||
rawActor.data.icon = undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
config.activitypub.discard_banners.find(
|
||||
instance => actor.id?.includes(instance)
|
||||
)
|
||||
) {
|
||||
rawActor.data.image = undefined;
|
||||
}
|
||||
|
||||
if (await rawActor.isObjectFiltered()) {
|
||||
return errorResponse("Actor filtered", 409);
|
||||
}
|
||||
|
||||
await rawActor.save();
|
||||
|
||||
this.actors.push(rawActor);
|
||||
|
||||
return rawActor;
|
||||
} else {
|
||||
if (this.actors.some(a => a.data.id === actor.id)) {
|
||||
return errorResponse("Actor already exists", 409);
|
||||
}
|
||||
|
||||
const rawActor = new RawActor();
|
||||
rawActor.data = actor;
|
||||
rawActor.followers = [];
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
if (
|
||||
config.activitypub.discard_avatars.find(
|
||||
instance => actor.id?.includes(instance)
|
||||
)
|
||||
) {
|
||||
rawActor.data.icon = undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
config.activitypub.discard_banners.find(
|
||||
instance => actor.id?.includes(instance)
|
||||
)
|
||||
) {
|
||||
rawActor.data.image = undefined;
|
||||
}
|
||||
|
||||
if (await rawActor.isObjectFiltered()) {
|
||||
return errorResponse("Actor filtered", 409);
|
||||
}
|
||||
|
||||
await rawActor.save();
|
||||
this.actors.push(rawActor);
|
||||
return rawActor;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ export class RawActor extends BaseEntity {
|
|||
@Column("jsonb")
|
||||
data!: APActor;
|
||||
|
||||
@Column("jsonb")
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
})
|
||||
followers!: string[];
|
||||
|
||||
static async getByActorId(id: string) {
|
||||
|
|
@ -33,6 +35,7 @@ export class RawActor extends BaseEntity {
|
|||
if (!(await RawActor.exists(data.id ?? ""))) {
|
||||
const actor = new RawActor();
|
||||
actor.data = data;
|
||||
actor.followers = [];
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ export class User extends BaseEntity {
|
|||
user.avatar = data.avatar ?? config.defaults.avatar;
|
||||
user.header = data.header ?? config.defaults.avatar;
|
||||
|
||||
user.followers = [];
|
||||
user.following = [];
|
||||
|
||||
await user.generateKeys();
|
||||
await user.updateActor();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue