mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Add new inbox endpoint
This commit is contained in:
parent
e618996936
commit
1027eada7c
27
database/entities/RawActivity.ts
Normal file
27
database/entities/RawActivity.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
ManyToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
import { APActivity } from "activitypub-types";
|
||||
import { RawObject } from "./RawObject";
|
||||
|
||||
/**
|
||||
* Stores an ActivityPub activity as raw JSON-LD data
|
||||
*/
|
||||
@Entity({
|
||||
name: "activities",
|
||||
})
|
||||
export class RawActivity extends BaseEntity {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
@Column("json")
|
||||
data!: APActivity;
|
||||
|
||||
// Any associated objects (there is typically only one)
|
||||
@ManyToMany(() => RawObject, object => object.id)
|
||||
objects!: RawObject[];
|
||||
}
|
||||
69
server/api/@[username]/inbox/index.ts
Normal file
69
server/api/@[username]/inbox/index.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { APActivity, APCreate, APObject } from "activitypub-types";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { RawActivity } from "~database/entities/RawActivity";
|
||||
import { RawObject } from "~database/entities/RawObject";
|
||||
|
||||
/**
|
||||
* ActivityPub user inbox endpoint
|
||||
*/
|
||||
export default async (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
): Promise<Response> => {
|
||||
// Check if POST request
|
||||
if (req.method !== "POST") {
|
||||
return errorResponse("Method not allowed", 405);
|
||||
}
|
||||
|
||||
// Process request body
|
||||
const body: APActivity = await req.json();
|
||||
|
||||
// Get the object's ActivityPub type
|
||||
const type = body.type;
|
||||
|
||||
switch (type) {
|
||||
case "Create" as APCreate: {
|
||||
// Body is an APCreate object
|
||||
// Store the Create object in database
|
||||
|
||||
// Check is Activity already exists
|
||||
const exists = await RawActivity.findOneBy({
|
||||
data: {
|
||||
id: body.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (exists) return errorResponse("Activity already exists", 409);
|
||||
|
||||
// Check if object already exists
|
||||
const objectExists = await RawObject.findOneBy({
|
||||
data: {
|
||||
id: (body.object as APObject).id,
|
||||
},
|
||||
});
|
||||
|
||||
if (objectExists)
|
||||
return errorResponse("Object already exists", 409);
|
||||
|
||||
const activity = new RawActivity();
|
||||
const object = new RawObject();
|
||||
|
||||
activity.data = {
|
||||
...body,
|
||||
object: undefined,
|
||||
};
|
||||
object.data = body.object as APObject;
|
||||
|
||||
activity.objects = [object];
|
||||
|
||||
// Save the new object and activity
|
||||
await object.save();
|
||||
await activity.save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return jsonResponse({});
|
||||
};
|
||||
|
|
@ -3,20 +3,15 @@ import { MatchedRoute } from "bun";
|
|||
import { User } from "~database/entities/User";
|
||||
import { getHost } from "@config";
|
||||
import { NodeObject, compact } from "jsonld";
|
||||
import { RawObject } from "~database/entities/RawObject";
|
||||
import { RawActivity } from "~database/entities/RawActivity";
|
||||
|
||||
/**
|
||||
* ActivityPub user inbox endpoint
|
||||
* ActivityPub user outbox endpoint
|
||||
*/
|
||||
export default async (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
): Promise<Response> => {
|
||||
// Check if POST request
|
||||
if (req.method !== "POST") {
|
||||
return errorResponse("Method not allowed", 405);
|
||||
}
|
||||
|
||||
const username = matchedRoute.params.username;
|
||||
const page = Boolean(matchedRoute.query.page || "false");
|
||||
const min_id = matchedRoute.query.min_id || false;
|
||||
|
|
@ -29,7 +24,7 @@ export default async (
|
|||
}
|
||||
|
||||
// Get the user's corresponding ActivityPub notes
|
||||
const count = await RawObject.count({
|
||||
const count = await RawActivity.count({
|
||||
where: {
|
||||
data: {
|
||||
attributedTo: `${getHost()}/@${user.username}`,
|
||||
|
|
@ -43,7 +38,7 @@ export default async (
|
|||
});
|
||||
|
||||
const lastPost = (
|
||||
await RawObject.find({
|
||||
await RawActivity.find({
|
||||
where: {
|
||||
data: {
|
||||
attributedTo: `${getHost()}/@${user.username}`,
|
||||
|
|
@ -75,10 +70,10 @@ export default async (
|
|||
})
|
||||
);
|
||||
else {
|
||||
let posts: RawObject[] = [];
|
||||
let posts: RawActivity[] = [];
|
||||
|
||||
if (min_id) {
|
||||
posts = await RawObject.find({
|
||||
posts = await RawActivity.find({
|
||||
where: {
|
||||
data: {
|
||||
attributedTo: `${getHost()}/@${user.username}`,
|
||||
|
|
@ -93,7 +88,7 @@ export default async (
|
|||
take: 11, // Take one extra to have the ID of the next post
|
||||
});
|
||||
} else if (max_id) {
|
||||
posts = await RawObject.find({
|
||||
posts = await RawActivity.find({
|
||||
where: {
|
||||
data: {
|
||||
attributedTo: `${getHost()}/@${user.username}`,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { errorResponse, jsonLdResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { RawObject } from "~database/entities/RawObject";
|
||||
import { RawActivity } from "~database/entities/RawActivity";
|
||||
|
||||
/**
|
||||
* Fetch a user
|
||||
|
|
@ -9,7 +9,7 @@ export default async (
|
|||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
): Promise<Response> => {
|
||||
const object = await RawObject.findOneBy({
|
||||
const object = await RawActivity.findOneBy({
|
||||
id: matchedRoute.params.id,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { APObject } from "activitypub-types";
|
||||
import { APActivity, APObject } from "activitypub-types";
|
||||
import { NodeObject } from "jsonld";
|
||||
|
||||
export const jsonResponse = (data: object, status = 200) => {
|
||||
|
|
@ -10,7 +10,10 @@ export const jsonResponse = (data: object, status = 200) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const jsonLdResponse = (data: NodeObject | APObject, status = 200) => {
|
||||
export const jsonLdResponse = (
|
||||
data: NodeObject | APActivity | APObject,
|
||||
status = 200
|
||||
) => {
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: {
|
||||
"Content-Type": "application/activity+json",
|
||||
|
|
|
|||
Loading…
Reference in a new issue