mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
Small refactors
This commit is contained in:
parent
2cadb68a56
commit
3b452d66aa
|
|
@ -12,6 +12,9 @@ import { RawActor } from "./RawActor";
|
|||
import { getConfig } from "@config";
|
||||
import { errorResponse } from "@response";
|
||||
|
||||
/**
|
||||
* Represents a raw activity entity in the database.
|
||||
*/
|
||||
@Entity({
|
||||
name: "activities",
|
||||
})
|
||||
|
|
@ -30,6 +33,11 @@ export class RawActivity extends BaseEntity {
|
|||
@JoinTable()
|
||||
actors!: RawActor[];
|
||||
|
||||
/**
|
||||
* Retrieves all activities that contain an object with the given ID.
|
||||
* @param id The ID of the object to search for.
|
||||
* @returns A promise that resolves to an array of matching activities.
|
||||
*/
|
||||
static async getByObjectId(id: string) {
|
||||
return await RawActivity.createQueryBuilder("activity")
|
||||
.leftJoinAndSelect("activity.objects", "objects")
|
||||
|
|
@ -38,6 +46,11 @@ export class RawActivity extends BaseEntity {
|
|||
.getMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the activity with the given ID.
|
||||
* @param id The ID of the activity to retrieve.
|
||||
* @returns A promise that resolves to the matching activity, or undefined if not found.
|
||||
*/
|
||||
static async getById(id: string) {
|
||||
return await RawActivity.createQueryBuilder("activity")
|
||||
.leftJoinAndSelect("activity.objects", "objects")
|
||||
|
|
@ -46,6 +59,11 @@ export class RawActivity extends BaseEntity {
|
|||
.getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the latest activity with the given ID.
|
||||
* @param id The ID of the activity to retrieve.
|
||||
* @returns A promise that resolves to the latest matching activity, or undefined if not found.
|
||||
*/
|
||||
static async getLatestById(id: string) {
|
||||
return await RawActivity.createQueryBuilder("activity")
|
||||
.where("activity.data->>'id' = :id", { id })
|
||||
|
|
@ -55,10 +73,20 @@ export class RawActivity extends BaseEntity {
|
|||
.getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an activity with the given ID exists.
|
||||
* @param id The ID of the activity to check for.
|
||||
* @returns A promise that resolves to true if the activity exists, false otherwise.
|
||||
*/
|
||||
static async exists(id: string) {
|
||||
return !!(await RawActivity.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an object in the database if it exists.
|
||||
* @param object The object to update.
|
||||
* @returns A promise that resolves to the updated object, or an error response if the object does not exist or is filtered.
|
||||
*/
|
||||
static async updateObjectIfExists(object: APObject) {
|
||||
const rawObject = await RawObject.getById(object.id ?? "");
|
||||
|
||||
|
|
@ -76,6 +104,11 @@ export class RawActivity extends BaseEntity {
|
|||
return rawObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an object from the database if it exists.
|
||||
* @param object The object to delete.
|
||||
* @returns A promise that resolves to the deleted object, or an error response if the object does not exist.
|
||||
*/
|
||||
static async deleteObjectIfExists(object: APObject) {
|
||||
const dbObject = await RawObject.getById(object.id ?? "");
|
||||
|
||||
|
|
@ -110,7 +143,16 @@ export class RawActivity extends BaseEntity {
|
|||
return dbObject;
|
||||
}
|
||||
|
||||
static async addIfNotExists(activity: APActivity, addObject?: RawObject) {
|
||||
/**
|
||||
* Adds an activity to the database if it does not already exist.
|
||||
* @param activity The activity to add.
|
||||
* @param addObject An optional object to add to the activity.
|
||||
* @returns A promise that resolves to the added activity, or an error response if the activity already exists or is filtered.
|
||||
*/
|
||||
static async createIfNotExists(
|
||||
activity: APActivity,
|
||||
addObject?: RawObject
|
||||
) {
|
||||
if (await RawActivity.exists(activity.id ?? "")) {
|
||||
return errorResponse("Activity already exists", 409);
|
||||
}
|
||||
|
|
@ -144,6 +186,10 @@ export class RawActivity extends BaseEntity {
|
|||
return rawActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ActivityPub representation of the activity.
|
||||
* @returns The ActivityPub representation of the activity.
|
||||
*/
|
||||
makeActivityPubRepresentation() {
|
||||
return {
|
||||
...this.data,
|
||||
|
|
@ -152,6 +198,11 @@ export class RawActivity extends BaseEntity {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object to the activity if it does not already exist.
|
||||
* @param object The object to add.
|
||||
* @returns A promise that resolves to the added object, or an error response if the object already exists or is filtered.
|
||||
*/
|
||||
async addObjectIfNotExists(object: APObject) {
|
||||
if (this.objects.some(o => o.data.id === object.id)) {
|
||||
return errorResponse("Object already exists", 409);
|
||||
|
|
@ -169,6 +220,11 @@ export class RawActivity extends BaseEntity {
|
|||
return rawObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an actor to the activity if it does not already exist.
|
||||
* @param actor The actor to add.
|
||||
* @returns A promise that resolves to the added actor, or an error response if the actor already exists or is filtered.
|
||||
*/
|
||||
async addActorIfNotExists(actor: APActor) {
|
||||
const dbActor = await RawActor.getByActorId(actor.id ?? "");
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,25 @@ export class User extends BaseEntity {
|
|||
return user;
|
||||
}
|
||||
|
||||
static async retrieveFromToken(access_token: string) {
|
||||
if (!access_token) return null;
|
||||
|
||||
const token = await Token.findOne({
|
||||
where: {
|
||||
access_token,
|
||||
},
|
||||
relations: {
|
||||
user: {
|
||||
relationships: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!token) return null;
|
||||
|
||||
return token.user;
|
||||
}
|
||||
|
||||
async getRelationshipToOtherUser(other: User) {
|
||||
const relationship = await Relationship.findOne({
|
||||
where: {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default async (
|
|||
// TODO: Add authentication
|
||||
|
||||
// Check is Activity already exists
|
||||
const activity = await RawActivity.addIfNotExists(body);
|
||||
const activity = await RawActivity.createIfNotExists(body);
|
||||
|
||||
if (activity instanceof Response) {
|
||||
return activity;
|
||||
|
|
@ -65,7 +65,7 @@ export default async (
|
|||
return object;
|
||||
}
|
||||
|
||||
const activity = await RawActivity.addIfNotExists(body, object);
|
||||
const activity = await RawActivity.createIfNotExists(body, object);
|
||||
|
||||
if (activity instanceof Response) {
|
||||
return activity;
|
||||
|
|
@ -87,7 +87,7 @@ export default async (
|
|||
}
|
||||
|
||||
// Store the Delete event in the database
|
||||
const activity = await RawActivity.addIfNotExists(body);
|
||||
const activity = await RawActivity.createIfNotExists(body);
|
||||
|
||||
if (activity instanceof Response) {
|
||||
return activity;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
|
|
@ -20,7 +19,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { RawActor } from "~database/entities/RawActor";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Fetch a user
|
||||
|
|
@ -13,8 +13,12 @@ export default async (
|
|||
const id = matchedRoute.params.id;
|
||||
|
||||
// Check auth token
|
||||
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
|
||||
const user = await getUserByToken(token);
|
||||
const token = req.headers.get("Authorization")?.split(" ")[1];
|
||||
|
||||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await User.retrieveFromToken(token);
|
||||
|
||||
let foundUser: RawActor | null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
|
|
@ -20,7 +19,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
|
|
@ -20,7 +19,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -19,7 +18,7 @@ export default async (
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
|
|
@ -14,7 +13,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
|
|
@ -14,7 +13,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const self = await getUserByToken(token);
|
||||
const self = await User.retrieveFromToken(token);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { getConfig } from "@config";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Patches a user
|
||||
|
|
@ -17,27 +17,35 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await getUserByToken(token);
|
||||
const user = await User.retrieveFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
const { display_name, note, avatar, header, locked, bot, discoverable } =
|
||||
await parseRequest<{
|
||||
display_name: string;
|
||||
note: string;
|
||||
avatar: File;
|
||||
header: File;
|
||||
locked: string;
|
||||
bot: string;
|
||||
discoverable: string;
|
||||
}>(req);
|
||||
|
||||
// TODO: Implement other options like field or source
|
||||
// const source_privacy = body.get("source[privacy]")?.toString() || null;
|
||||
// const source_sensitive = body.get("source[sensitive]")?.toString() || null;
|
||||
// const source_language = body.get("source[language]")?.toString() || null;
|
||||
const {
|
||||
display_name,
|
||||
note,
|
||||
avatar,
|
||||
header,
|
||||
locked,
|
||||
bot,
|
||||
discoverable,
|
||||
"source[privacy]": source_privacy,
|
||||
"source[sensitive]": source_sensitive,
|
||||
"source[language]": source_language,
|
||||
} = await parseRequest<{
|
||||
display_name: string;
|
||||
note: string;
|
||||
avatar: File;
|
||||
header: File;
|
||||
locked: string;
|
||||
bot: string;
|
||||
discoverable: string;
|
||||
"source[privacy]": string;
|
||||
"source[sensitive]": string;
|
||||
"source[language]": string;
|
||||
}>(req);
|
||||
|
||||
if (display_name) {
|
||||
// Check if within allowed display name lengths
|
||||
|
|
@ -66,6 +74,36 @@ export default async (req: Request): Promise<Response> => {
|
|||
user.note = note;
|
||||
}
|
||||
|
||||
if (source_privacy) {
|
||||
// Check if within allowed privacy values
|
||||
if (
|
||||
!["public", "unlisted", "private", "direct"].includes(
|
||||
source_privacy
|
||||
)
|
||||
) {
|
||||
return errorResponse(
|
||||
"Privacy must be one of public, unlisted, private, or direct",
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
user.source.privacy = source_privacy;
|
||||
}
|
||||
|
||||
if (source_sensitive) {
|
||||
// Check if within allowed sensitive values
|
||||
if (source_sensitive !== "true" && source_sensitive !== "false") {
|
||||
return errorResponse("Sensitive must be a boolean", 422);
|
||||
}
|
||||
|
||||
user.source.sensitive = source_sensitive === "true";
|
||||
}
|
||||
|
||||
if (source_language) {
|
||||
// TODO: Check if proper ISO code
|
||||
user.source.language = source_language;
|
||||
}
|
||||
|
||||
if (avatar) {
|
||||
// Check if within allowed avatar length (avatar is an image)
|
||||
if (avatar.size > config.validation.max_avatar_size) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Patches a user
|
||||
|
|
@ -16,7 +16,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await getUserByToken(token);
|
||||
const user = await User.retrieveFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Returns OAuth2 credentials
|
||||
|
|
@ -12,7 +12,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await getUserByToken(token);
|
||||
const user = await User.retrieveFromToken(token);
|
||||
const application = await Application.getFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { getUserByToken } from "@auth";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { RawObject } from "~database/entities/RawObject";
|
||||
import { Status } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Fetch a user
|
||||
|
|
@ -15,7 +15,11 @@ export default async (
|
|||
|
||||
// Check auth token
|
||||
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
|
||||
const user = await getUserByToken(token);
|
||||
|
||||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await User.retrieveFromToken(token);
|
||||
|
||||
// TODO: Add checks for user's permissions to view this status
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { getUserByToken } from "@auth";
|
||||
import { getConfig } from "@config";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { Status } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
|
||||
/**
|
||||
* Post new status
|
||||
|
|
@ -20,7 +20,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
if (!token)
|
||||
return errorResponse("This method requires an authenticated user", 422);
|
||||
|
||||
const user = await getUserByToken(token);
|
||||
const user = await User.retrieveFromToken(token);
|
||||
const application = await Application.getFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import { Token } from "~database/entities/Token";
|
||||
|
||||
export const getUserByToken = async (access_token: string | null) => {
|
||||
if (!access_token) return null;
|
||||
|
||||
const token = await Token.findOne({
|
||||
where: {
|
||||
access_token,
|
||||
},
|
||||
relations: {
|
||||
user: {
|
||||
relationships: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!token) return null;
|
||||
|
||||
return token.user;
|
||||
};
|
||||
Loading…
Reference in a new issue