mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
HUGE rewrite to use Prisma instead of TypeORM (not finished yet)
This commit is contained in:
parent
a1c0164e9d
commit
5eed8374cd
|
|
@ -2,10 +2,10 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { getConfig } from "@config";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { RawActivity } from "~database/entities/RawActivity";
|
||||
import { Token, TokenType } from "~database/entities/Token";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
@ -13,14 +13,14 @@ let token: Token;
|
|||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||
|
||||
// Initialize test user
|
||||
const user = await User.createNewLocal({
|
||||
const user = await UserAction.createNewLocal({
|
||||
email: "test@test.com",
|
||||
username: "test",
|
||||
password: "test",
|
||||
display_name: "",
|
||||
});
|
||||
|
||||
const app = new Application();
|
||||
const app = new ApplicationAction();
|
||||
|
||||
app.name = "Test Application";
|
||||
app.website = "https://example.com";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { DataSource } from "typeorm";
|
||||
import { getConfig } from "../utils/config";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
@ -14,4 +15,8 @@ const AppDataSource = new DataSource({
|
|||
entities: [process.cwd() + "/database/entities/*.ts"],
|
||||
});
|
||||
|
||||
export { AppDataSource };
|
||||
const client = new PrismaClient({
|
||||
datasourceUrl: `postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.database}`,
|
||||
});
|
||||
|
||||
export { AppDataSource, client };
|
||||
|
|
|
|||
|
|
@ -1,76 +1,42 @@
|
|||
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { APIApplication } from "~types/entities/application";
|
||||
import { Token } from "./Token";
|
||||
import { Application } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
|
||||
/**
|
||||
* Represents an application that can authenticate with the API.
|
||||
*/
|
||||
@Entity({
|
||||
name: "applications",
|
||||
})
|
||||
export class Application extends BaseEntity {
|
||||
/** The unique identifier for this application. */
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/** The name of this application. */
|
||||
@Column("varchar")
|
||||
name!: string;
|
||||
/**
|
||||
* Retrieves the application associated with the given access token.
|
||||
* @param token The access token to retrieve the application for.
|
||||
* @returns The application associated with the given access token, or null if no such application exists.
|
||||
*/
|
||||
export const getFromToken = async (
|
||||
token: string
|
||||
): Promise<Application | null> => {
|
||||
const dbToken = await client.token.findFirst({
|
||||
where: {
|
||||
access_token: token,
|
||||
},
|
||||
include: {
|
||||
application: true,
|
||||
},
|
||||
});
|
||||
|
||||
/** The website associated with this application, if any. */
|
||||
@Column("varchar", {
|
||||
nullable: true,
|
||||
})
|
||||
website!: string | null;
|
||||
return dbToken?.application || null;
|
||||
};
|
||||
|
||||
/** The VAPID key associated with this application, if any. */
|
||||
@Column("varchar", {
|
||||
nullable: true,
|
||||
})
|
||||
vapid_key!: string | null;
|
||||
|
||||
/** The client ID associated with this application. */
|
||||
@Column("varchar")
|
||||
client_id!: string;
|
||||
|
||||
/** The secret associated with this application. */
|
||||
@Column("varchar")
|
||||
secret!: string;
|
||||
|
||||
/** The scopes associated with this application. */
|
||||
@Column("varchar")
|
||||
scopes = "read";
|
||||
|
||||
/** The redirect URIs associated with this application. */
|
||||
@Column("varchar")
|
||||
redirect_uris = "urn:ietf:wg:oauth:2.0:oob";
|
||||
|
||||
/**
|
||||
* Retrieves the application associated with the given access token.
|
||||
* @param token The access token to retrieve the application for.
|
||||
* @returns The application associated with the given access token, or null if no such application exists.
|
||||
*/
|
||||
static async getFromToken(token: string): Promise<Application | null> {
|
||||
const dbToken = await Token.findOne({
|
||||
where: {
|
||||
access_token: token,
|
||||
},
|
||||
relations: ["application"],
|
||||
});
|
||||
|
||||
return dbToken?.application || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this application to an API application.
|
||||
* @returns The API application representation of this application.
|
||||
*/
|
||||
/**
|
||||
* Converts this application to an API application.
|
||||
* @returns The API application representation of this application.
|
||||
*/
|
||||
export const applicationToAPI = async (
|
||||
app: Application
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async toAPI(): Promise<APIApplication> {
|
||||
return {
|
||||
name: this.name,
|
||||
website: this.website,
|
||||
vapid_key: this.vapid_key,
|
||||
};
|
||||
}
|
||||
}
|
||||
): Promise<APIApplication> => {
|
||||
return {
|
||||
name: app.name,
|
||||
website: app.website,
|
||||
vapid_key: app.vapid_key,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,137 +1,78 @@
|
|||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
IsNull,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
import { APIEmoji } from "~types/entities/emoji";
|
||||
import { Instance } from "./Instance";
|
||||
import { Emoji as LysandEmoji } from "~types/lysand/extensions/org.lysand/custom_emojis";
|
||||
import { client } from "~database/datasource";
|
||||
import { Emoji } from "@prisma/client";
|
||||
|
||||
/**
|
||||
* Represents an emoji entity in the database.
|
||||
*/
|
||||
@Entity({
|
||||
name: "emojis",
|
||||
})
|
||||
export class Emoji extends BaseEntity {
|
||||
/**
|
||||
* The unique identifier for the emoji.
|
||||
*/
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/**
|
||||
* The shortcode for the emoji.
|
||||
*/
|
||||
@Column("varchar")
|
||||
shortcode!: string;
|
||||
|
||||
/**
|
||||
* The instance that the emoji is from.
|
||||
* If is null, the emoji is from the server's instance
|
||||
*/
|
||||
@ManyToOne(() => Instance, {
|
||||
nullable: true,
|
||||
})
|
||||
instance!: Instance | null;
|
||||
|
||||
/**
|
||||
* The URL for the emoji.
|
||||
*/
|
||||
@Column("varchar")
|
||||
url!: string;
|
||||
|
||||
/**
|
||||
* The alt text for the emoji.
|
||||
*/
|
||||
@Column("varchar", {
|
||||
nullable: true,
|
||||
})
|
||||
alt!: string | null;
|
||||
|
||||
/**
|
||||
* The content type of the emoji.
|
||||
*/
|
||||
@Column("varchar")
|
||||
content_type!: string;
|
||||
|
||||
/**
|
||||
* Whether the emoji is visible in the picker.
|
||||
*/
|
||||
@Column("boolean")
|
||||
visible_in_picker!: boolean;
|
||||
|
||||
/**
|
||||
* Used for parsing emojis from local text
|
||||
* @param text The text to parse
|
||||
* @returns An array of emojis
|
||||
*/
|
||||
static async parseEmojis(text: string): Promise<Emoji[]> {
|
||||
const regex = /:[a-zA-Z0-9_]+:/g;
|
||||
const matches = text.match(regex);
|
||||
if (!matches) return [];
|
||||
return (
|
||||
await Promise.all(
|
||||
matches.map(match =>
|
||||
Emoji.findOne({
|
||||
where: {
|
||||
shortcode: match.slice(1, -1),
|
||||
instance: IsNull(),
|
||||
},
|
||||
relations: ["instance"],
|
||||
})
|
||||
)
|
||||
)
|
||||
).filter(emoji => emoji !== null) as Emoji[];
|
||||
}
|
||||
|
||||
static async addIfNotExists(emoji: LysandEmoji) {
|
||||
const existingEmoji = await Emoji.findOne({
|
||||
where: {
|
||||
shortcode: emoji.name,
|
||||
instance: IsNull(),
|
||||
/**
|
||||
* Used for parsing emojis from local text
|
||||
* @param text The text to parse
|
||||
* @returns An array of emojis
|
||||
*/
|
||||
export const parseEmojis = async (text: string): Promise<Emoji[]> => {
|
||||
const regex = /:[a-zA-Z0-9_]+:/g;
|
||||
const matches = text.match(regex);
|
||||
if (!matches) return [];
|
||||
return await client.emoji.findMany({
|
||||
where: {
|
||||
shortcode: {
|
||||
in: matches.map(match => match.replace(/:/g, "")),
|
||||
},
|
||||
});
|
||||
if (existingEmoji) return existingEmoji;
|
||||
const newEmoji = new Emoji();
|
||||
newEmoji.shortcode = emoji.name;
|
||||
// TODO: Content types
|
||||
newEmoji.url = emoji.url[0].content;
|
||||
newEmoji.alt = emoji.alt || null;
|
||||
newEmoji.content_type = emoji.url[0].content_type;
|
||||
newEmoji.visible_in_picker = true;
|
||||
await newEmoji.save();
|
||||
return newEmoji;
|
||||
}
|
||||
},
|
||||
include: {
|
||||
instance: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the emoji to an APIEmoji object.
|
||||
* @returns The APIEmoji object.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async toAPI(): Promise<APIEmoji> {
|
||||
return {
|
||||
shortcode: this.shortcode,
|
||||
static_url: this.url, // TODO: Add static version
|
||||
url: this.url,
|
||||
visible_in_picker: this.visible_in_picker,
|
||||
category: undefined,
|
||||
};
|
||||
}
|
||||
export const addEmojiIfNotExists = async (emoji: LysandEmoji) => {
|
||||
const existingEmoji = await client.emoji.findFirst({
|
||||
where: {
|
||||
shortcode: emoji.name,
|
||||
instance: null,
|
||||
},
|
||||
});
|
||||
|
||||
toLysand(): LysandEmoji {
|
||||
return {
|
||||
name: this.shortcode,
|
||||
url: [
|
||||
{
|
||||
content: this.url,
|
||||
content_type: this.content_type,
|
||||
},
|
||||
],
|
||||
alt: this.alt || undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (existingEmoji) return existingEmoji;
|
||||
|
||||
return await client.emoji.create({
|
||||
data: {
|
||||
shortcode: emoji.name,
|
||||
url: emoji.url[0].content,
|
||||
alt: emoji.alt || null,
|
||||
content_type: emoji.url[0].content_type,
|
||||
visible_in_picker: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the emoji to an APIEmoji object.
|
||||
* @returns The APIEmoji object.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export const emojiToAPI = async (emoji: Emoji): Promise<APIEmoji> => {
|
||||
return {
|
||||
shortcode: emoji.shortcode,
|
||||
static_url: emoji.url, // TODO: Add static version
|
||||
url: emoji.url,
|
||||
visible_in_picker: emoji.visible_in_picker,
|
||||
category: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const emojiToLysand = (emoji: Emoji): LysandEmoji => {
|
||||
return {
|
||||
name: emoji.shortcode,
|
||||
url: [
|
||||
{
|
||||
content: emoji.url,
|
||||
content_type: emoji.content_type,
|
||||
},
|
||||
],
|
||||
alt: emoji.alt || undefined,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,90 +1,49 @@
|
|||
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { ContentFormat, ServerMetadata } from "~types/lysand/Object";
|
||||
import { Instance } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
import { ServerMetadata } from "~types/lysand/Object";
|
||||
|
||||
/**
|
||||
* Represents an instance in the database.
|
||||
*/
|
||||
@Entity({
|
||||
name: "instances",
|
||||
})
|
||||
export class Instance extends BaseEntity {
|
||||
/**
|
||||
* The unique identifier of the instance.
|
||||
*/
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/**
|
||||
* The base URL of the instance.
|
||||
* Must not have the https:// or http:// prefix.
|
||||
*/
|
||||
@Column("varchar")
|
||||
base_url!: string;
|
||||
/**
|
||||
* Adds an instance to the database if it doesn't already exist.
|
||||
* @param url
|
||||
* @returns Either the database instance if it already exists, or a newly created instance.
|
||||
*/
|
||||
export const addInstanceIfNotExists = async (
|
||||
url: string
|
||||
): Promise<Instance> => {
|
||||
const origin = new URL(url).origin;
|
||||
const hostname = new URL(url).hostname;
|
||||
|
||||
/**
|
||||
* The name of the instance.
|
||||
*/
|
||||
@Column("varchar")
|
||||
name!: string;
|
||||
const found = await client.instance.findFirst({
|
||||
where: {
|
||||
base_url: hostname,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* The description of the instance.
|
||||
*/
|
||||
@Column("varchar")
|
||||
version!: string;
|
||||
if (found) return found;
|
||||
|
||||
/**
|
||||
* The logo of the instance.
|
||||
*/
|
||||
@Column("jsonb")
|
||||
logo?: ContentFormat[];
|
||||
// Fetch the instance configuration
|
||||
const metadata = (await fetch(`${origin}/.well-known/lysand`).then(res =>
|
||||
res.json()
|
||||
)) as Partial<ServerMetadata>;
|
||||
|
||||
/**
|
||||
* The banner of the instance.
|
||||
*/
|
||||
banner?: ContentFormat[];
|
||||
|
||||
/**
|
||||
* Adds an instance to the database if it doesn't already exist.
|
||||
* @param url
|
||||
* @returns Either the database instance if it already exists, or a newly created instance.
|
||||
*/
|
||||
static async addIfNotExists(url: string): Promise<Instance> {
|
||||
const origin = new URL(url).origin;
|
||||
const hostname = new URL(url).hostname;
|
||||
|
||||
const found = await Instance.findOne({
|
||||
where: {
|
||||
base_url: hostname,
|
||||
},
|
||||
});
|
||||
|
||||
if (found) return found;
|
||||
|
||||
const instance = new Instance();
|
||||
|
||||
instance.base_url = hostname;
|
||||
|
||||
// Fetch the instance configuration
|
||||
const metadata = (await fetch(`${origin}/.well-known/lysand`).then(
|
||||
res => res.json()
|
||||
)) as Partial<ServerMetadata>;
|
||||
|
||||
if (metadata.type !== "ServerMetadata") {
|
||||
throw new Error("Invalid instance metadata");
|
||||
}
|
||||
|
||||
if (!(metadata.name && metadata.version)) {
|
||||
throw new Error("Invalid instance metadata");
|
||||
}
|
||||
|
||||
instance.name = metadata.name;
|
||||
instance.version = metadata.version;
|
||||
instance.logo = metadata.logo;
|
||||
instance.banner = metadata.banner;
|
||||
|
||||
await instance.save();
|
||||
|
||||
return instance;
|
||||
if (metadata.type !== "ServerMetadata") {
|
||||
throw new Error("Invalid instance metadata");
|
||||
}
|
||||
}
|
||||
|
||||
if (!(metadata.name && metadata.version)) {
|
||||
throw new Error("Invalid instance metadata");
|
||||
}
|
||||
|
||||
return await client.instance.create({
|
||||
data: {
|
||||
base_url: hostname,
|
||||
name: metadata.name,
|
||||
version: metadata.version,
|
||||
logo: metadata.logo as any,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,45 +1,19 @@
|
|||
import {
|
||||
BaseEntity,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
import { User } from "./User";
|
||||
import { Status } from "./Status";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { Like as LysandLike } from "~types/lysand/Object";
|
||||
import { getConfig } from "@config";
|
||||
import { Like } from "@prisma/client";
|
||||
|
||||
/**
|
||||
* Represents a Like entity in the database.
|
||||
*/
|
||||
@Entity({
|
||||
name: "likes",
|
||||
})
|
||||
export class Like extends BaseEntity {
|
||||
/** The unique identifier of the Like. */
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/** The User who liked the Status. */
|
||||
@ManyToOne(() => User)
|
||||
liker!: User;
|
||||
|
||||
/** The Status that was liked. */
|
||||
@ManyToOne(() => Status)
|
||||
liked!: Status;
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at!: Date;
|
||||
|
||||
toLysand(): LysandLike {
|
||||
return {
|
||||
id: this.id,
|
||||
author: this.liker.uri,
|
||||
type: "Like",
|
||||
created_at: new Date(this.created_at).toISOString(),
|
||||
object: this.liked.toLysand().uri,
|
||||
uri: `${getConfig().http.base_url}/actions/${this.id}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
export const toLysand = (like: Like): LysandLike => {
|
||||
return {
|
||||
id: like.id,
|
||||
author: (like as any).liker?.uri,
|
||||
type: "Like",
|
||||
created_at: new Date(like.createdAt).toISOString(),
|
||||
object: (like as any).liked?.uri,
|
||||
uri: `${getConfig().http.base_url}/actions/${like.id}`,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,147 +1,87 @@
|
|||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { LysandObject } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
import { LysandObjectType } from "~types/lysand/Object";
|
||||
|
||||
/**
|
||||
* Represents a Lysand object in the database.
|
||||
*/
|
||||
@Entity({
|
||||
name: "objects",
|
||||
})
|
||||
export class LysandObject extends BaseEntity {
|
||||
/**
|
||||
* The unique identifier for the object. If local, same as `remote_id`
|
||||
*/
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/**
|
||||
* UUID of the object across the network. If the object is local, same as `id`
|
||||
*/
|
||||
remote_id!: string;
|
||||
export const createFromObject = async (object: LysandObjectType) => {
|
||||
const foundObject = await client.lysandObject.findFirst({
|
||||
where: { remote_id: object.id },
|
||||
include: {
|
||||
author: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Any valid Lysand type, such as `Note`, `Like`, `Follow`, etc.
|
||||
*/
|
||||
@Column("varchar")
|
||||
type!: string;
|
||||
|
||||
/**
|
||||
* Remote URI for the object
|
||||
* Example: `https://example.com/publications/ef235cc6-d68c-4756-b0df-4e6623c4d51c`
|
||||
*/
|
||||
@Column("varchar")
|
||||
uri!: string;
|
||||
|
||||
@Column("timestamp")
|
||||
created_at!: Date;
|
||||
|
||||
/**
|
||||
* References an Actor object
|
||||
*/
|
||||
@ManyToOne(() => LysandObject, object => object.uri, {
|
||||
nullable: true,
|
||||
})
|
||||
author!: LysandObject | null;
|
||||
|
||||
@Column("jsonb")
|
||||
extra_data!: Omit<
|
||||
Omit<Omit<Omit<LysandObjectType, "created_at">, "id">, "uri">,
|
||||
"type"
|
||||
>;
|
||||
|
||||
@Column("jsonb")
|
||||
extensions!: Record<string, any>;
|
||||
|
||||
static new(type: string, uri: string): LysandObject {
|
||||
const object = new LysandObject();
|
||||
object.type = type;
|
||||
object.uri = uri;
|
||||
object.created_at = new Date();
|
||||
return object;
|
||||
if (foundObject) {
|
||||
return foundObject;
|
||||
}
|
||||
|
||||
static async createFromObject(object: LysandObjectType) {
|
||||
let newObject: LysandObject;
|
||||
const author = await client.lysandObject.findFirst({
|
||||
where: { uri: (object as any).author },
|
||||
});
|
||||
|
||||
const foundObject = await LysandObject.findOne({
|
||||
where: { remote_id: object.id },
|
||||
relations: ["author"],
|
||||
});
|
||||
return await client.lysandObject.create({
|
||||
data: {
|
||||
authorId: author?.id,
|
||||
created_at: new Date(object.created_at),
|
||||
extensions: object.extensions || {},
|
||||
remote_id: object.id,
|
||||
type: object.type,
|
||||
uri: object.uri,
|
||||
// Rest of data (remove id, author, created_at, extensions, type, uri)
|
||||
extra_data: Object.fromEntries(
|
||||
Object.entries(object).filter(
|
||||
([key]) =>
|
||||
![
|
||||
"id",
|
||||
"author",
|
||||
"created_at",
|
||||
"extensions",
|
||||
"type",
|
||||
"uri",
|
||||
].includes(key)
|
||||
)
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (foundObject) {
|
||||
newObject = foundObject;
|
||||
} else {
|
||||
newObject = new LysandObject();
|
||||
}
|
||||
export const toLysand = (lyObject: LysandObject): LysandObjectType => {
|
||||
return {
|
||||
id: lyObject.remote_id || lyObject.id,
|
||||
created_at: new Date(lyObject.created_at).toISOString(),
|
||||
type: lyObject.type,
|
||||
uri: lyObject.uri,
|
||||
// @ts-expect-error This works, I promise
|
||||
...lyObject.extra_data,
|
||||
extensions: lyObject.extensions,
|
||||
};
|
||||
};
|
||||
|
||||
const author = await LysandObject.findOne({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
where: { uri: (object as any).author },
|
||||
});
|
||||
export const isPublication = (lyObject: LysandObject): boolean => {
|
||||
return lyObject.type === "Note" || lyObject.type === "Patch";
|
||||
};
|
||||
|
||||
newObject.author = author;
|
||||
newObject.created_at = new Date(object.created_at);
|
||||
newObject.extensions = object.extensions || {};
|
||||
newObject.remote_id = object.id;
|
||||
newObject.type = object.type;
|
||||
newObject.uri = object.uri;
|
||||
// Rest of data (remove id, author, created_at, extensions, type, uri)
|
||||
newObject.extra_data = Object.fromEntries(
|
||||
Object.entries(object).filter(
|
||||
([key]) =>
|
||||
![
|
||||
"id",
|
||||
"author",
|
||||
"created_at",
|
||||
"extensions",
|
||||
"type",
|
||||
"uri",
|
||||
].includes(key)
|
||||
)
|
||||
);
|
||||
export const isAction = (lyObject: LysandObject): boolean => {
|
||||
return [
|
||||
"Like",
|
||||
"Follow",
|
||||
"Dislike",
|
||||
"FollowAccept",
|
||||
"FollowReject",
|
||||
"Undo",
|
||||
"Announce",
|
||||
].includes(lyObject.type);
|
||||
};
|
||||
|
||||
await newObject.save();
|
||||
return newObject;
|
||||
}
|
||||
export const isActor = (lyObject: LysandObject): boolean => {
|
||||
return lyObject.type === "User";
|
||||
};
|
||||
|
||||
toLysand(): LysandObjectType {
|
||||
return {
|
||||
id: this.remote_id || this.id,
|
||||
created_at: new Date(this.created_at).toISOString(),
|
||||
type: this.type,
|
||||
uri: this.uri,
|
||||
...this.extra_data,
|
||||
extensions: this.extensions,
|
||||
};
|
||||
}
|
||||
|
||||
isPublication(): boolean {
|
||||
return this.type === "Note" || this.type === "Patch";
|
||||
}
|
||||
|
||||
isAction(): boolean {
|
||||
return [
|
||||
"Like",
|
||||
"Follow",
|
||||
"Dislike",
|
||||
"FollowAccept",
|
||||
"FollowReject",
|
||||
"Undo",
|
||||
"Announce",
|
||||
].includes(this.type);
|
||||
}
|
||||
|
||||
isActor(): boolean {
|
||||
return this.type === "User";
|
||||
}
|
||||
|
||||
isExtension(): boolean {
|
||||
return this.type === "Extension";
|
||||
}
|
||||
}
|
||||
export const isExtension = (lyObject: LysandObject): boolean => {
|
||||
return lyObject.type === "Extension";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,144 +1,63 @@
|
|||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm";
|
||||
import { User } from "./User";
|
||||
import { Relationship, User } from "@prisma/client";
|
||||
import { APIRelationship } from "~types/entities/relationship";
|
||||
import { client } from "~database/datasource";
|
||||
|
||||
/**
|
||||
* Stores Mastodon API relationships
|
||||
*/
|
||||
@Entity({
|
||||
name: "relationships",
|
||||
})
|
||||
export class Relationship extends BaseEntity {
|
||||
/** The unique identifier for the relationship. */
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/** The user who owns the relationship. */
|
||||
@ManyToOne(() => User, user => user.relationships)
|
||||
owner!: User;
|
||||
/**
|
||||
* Creates a new relationship between two users.
|
||||
* @param owner The user who owns the relationship.
|
||||
* @param other The user who is the subject of the relationship.
|
||||
* @returns The newly created relationship.
|
||||
*/
|
||||
export const createNew = async (
|
||||
owner: User,
|
||||
other: User
|
||||
): Promise<Relationship> => {
|
||||
return await client.relationship.create({
|
||||
data: {
|
||||
ownerId: owner.id,
|
||||
subjectId: other.id,
|
||||
languages: [],
|
||||
following: false,
|
||||
showingReblogs: false,
|
||||
notifying: false,
|
||||
followedBy: false,
|
||||
blocking: false,
|
||||
blockedBy: false,
|
||||
muting: false,
|
||||
mutingNotifications: false,
|
||||
requested: false,
|
||||
domainBlocking: false,
|
||||
endorsed: false,
|
||||
note: "",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** The user who is the subject of the relationship. */
|
||||
@ManyToOne(() => User)
|
||||
subject!: User;
|
||||
|
||||
/** Whether the owner is following the subject. */
|
||||
@Column("boolean")
|
||||
following!: boolean;
|
||||
|
||||
/** Whether the owner is showing reblogs from the subject. */
|
||||
@Column("boolean")
|
||||
showing_reblogs!: boolean;
|
||||
|
||||
/** Whether the owner is receiving notifications from the subject. */
|
||||
@Column("boolean")
|
||||
notifying!: boolean;
|
||||
|
||||
/** Whether the owner is followed by the subject. */
|
||||
@Column("boolean")
|
||||
followed_by!: boolean;
|
||||
|
||||
/** Whether the owner is blocking the subject. */
|
||||
@Column("boolean")
|
||||
blocking!: boolean;
|
||||
|
||||
/** Whether the owner is blocked by the subject. */
|
||||
@Column("boolean")
|
||||
blocked_by!: boolean;
|
||||
|
||||
/** Whether the owner is muting the subject. */
|
||||
@Column("boolean")
|
||||
muting!: boolean;
|
||||
|
||||
/** Whether the owner is muting notifications from the subject. */
|
||||
@Column("boolean")
|
||||
muting_notifications!: boolean;
|
||||
|
||||
/** Whether the owner has requested to follow the subject. */
|
||||
@Column("boolean")
|
||||
requested!: boolean;
|
||||
|
||||
/** Whether the owner is blocking the subject's domain. */
|
||||
@Column("boolean")
|
||||
domain_blocking!: boolean;
|
||||
|
||||
/** Whether the owner has endorsed the subject. */
|
||||
@Column("boolean")
|
||||
endorsed!: boolean;
|
||||
|
||||
/** The languages the owner has specified for the subject. */
|
||||
@Column("jsonb")
|
||||
languages!: string[];
|
||||
|
||||
/** A note the owner has added for the subject. */
|
||||
@Column("varchar")
|
||||
note!: string;
|
||||
|
||||
/** The date the relationship was created. */
|
||||
@CreateDateColumn()
|
||||
created_at!: Date;
|
||||
|
||||
/** The date the relationship was last updated. */
|
||||
@UpdateDateColumn()
|
||||
updated_at!: Date;
|
||||
|
||||
/**
|
||||
* Creates a new relationship between two users.
|
||||
* @param owner The user who owns the relationship.
|
||||
* @param other The user who is the subject of the relationship.
|
||||
* @returns The newly created relationship.
|
||||
*/
|
||||
static async createNew(owner: User, other: User): Promise<Relationship> {
|
||||
const newRela = new Relationship();
|
||||
newRela.owner = owner;
|
||||
newRela.subject = other;
|
||||
newRela.languages = [];
|
||||
newRela.following = false;
|
||||
newRela.showing_reblogs = false;
|
||||
newRela.notifying = false;
|
||||
newRela.followed_by = false;
|
||||
newRela.blocking = false;
|
||||
newRela.blocked_by = false;
|
||||
newRela.muting = false;
|
||||
newRela.muting_notifications = false;
|
||||
newRela.requested = false;
|
||||
newRela.domain_blocking = false;
|
||||
newRela.endorsed = false;
|
||||
newRela.note = "";
|
||||
|
||||
await newRela.save();
|
||||
|
||||
return newRela;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the relationship to an API-friendly format.
|
||||
* @returns The API-friendly relationship.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async toAPI(): Promise<APIRelationship> {
|
||||
return {
|
||||
blocked_by: this.blocked_by,
|
||||
blocking: this.blocking,
|
||||
domain_blocking: this.domain_blocking,
|
||||
endorsed: this.endorsed,
|
||||
followed_by: this.followed_by,
|
||||
following: this.following,
|
||||
id: this.subject.id,
|
||||
muting: this.muting,
|
||||
muting_notifications: this.muting_notifications,
|
||||
notifying: this.notifying,
|
||||
requested: this.requested,
|
||||
showing_reblogs: this.showing_reblogs,
|
||||
languages: this.languages,
|
||||
note: this.note,
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts the relationship to an API-friendly format.
|
||||
* @returns The API-friendly relationship.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export const toAPI = async (rel: Relationship): Promise<APIRelationship> => {
|
||||
return {
|
||||
blocked_by: rel.blockedBy,
|
||||
blocking: rel.blocking,
|
||||
domain_blocking: rel.domainBlocking,
|
||||
endorsed: rel.endorsed,
|
||||
followed_by: rel.followedBy,
|
||||
following: rel.following,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
id: (rel as any).subject.id,
|
||||
muting: rel.muting,
|
||||
muting_notifications: rel.mutingNotifications,
|
||||
notifying: rel.notifying,
|
||||
requested: rel.requested,
|
||||
showing_reblogs: rel.showingReblogs,
|
||||
languages: rel.languages,
|
||||
note: rel.note,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,54 +1,3 @@
|
|||
import {
|
||||
Entity,
|
||||
BaseEntity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm";
|
||||
import { User } from "./User";
|
||||
import { Application } from "./Application";
|
||||
|
||||
/**
|
||||
* Represents an access token for a user or application.
|
||||
*/
|
||||
@Entity({
|
||||
name: "tokens",
|
||||
})
|
||||
export class Token extends BaseEntity {
|
||||
/** The unique identifier for the token. */
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id!: string;
|
||||
|
||||
/** The type of token. */
|
||||
@Column("varchar")
|
||||
token_type: TokenType = TokenType.BEARER;
|
||||
|
||||
/** The scope of the token. */
|
||||
@Column("varchar")
|
||||
scope!: string;
|
||||
|
||||
/** The access token string. */
|
||||
@Column("varchar")
|
||||
access_token!: string;
|
||||
|
||||
/** The authorization code used to obtain the token. */
|
||||
@Column("varchar")
|
||||
code!: string;
|
||||
|
||||
/** The date and time the token was created. */
|
||||
@CreateDateColumn()
|
||||
created_at!: Date;
|
||||
|
||||
/** The user associated with the token. */
|
||||
@ManyToOne(() => User, user => user.id)
|
||||
user!: User;
|
||||
|
||||
/** The application associated with the token. */
|
||||
@ManyToOne(() => Application, application => application.id)
|
||||
application!: Application;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of token.
|
||||
*/
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
17
index.ts
17
index.ts
|
|
@ -6,7 +6,7 @@ import { appendFile } from "fs/promises";
|
|||
import { matches } from "ip-matching";
|
||||
import "reflect-metadata";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { User } from "~database/entities/User";
|
||||
import { AuthData, UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
const router = new Bun.FileSystemRouter({
|
||||
|
|
@ -60,7 +60,8 @@ Bun.serve({
|
|||
meta: APIRouteMeta;
|
||||
default: (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
matchedRoute: MatchedRoute,
|
||||
auth: AuthData
|
||||
) => Response | Promise<Response>;
|
||||
} = await import(matchedRoute.filePath);
|
||||
|
||||
|
|
@ -78,11 +79,11 @@ Bun.serve({
|
|||
|
||||
// TODO: Check for ratelimits
|
||||
|
||||
const auth = await UserAction.getFromRequest(req);
|
||||
|
||||
// Check for authentication if required
|
||||
if (meta.auth.required) {
|
||||
const { user } = await User.getFromRequest(req);
|
||||
|
||||
if (!user) {
|
||||
if (!auth.user) {
|
||||
return new Response(undefined, {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
|
|
@ -91,9 +92,7 @@ Bun.serve({
|
|||
} else if (
|
||||
(meta.auth.requiredOnMethods ?? []).includes(req.method as any)
|
||||
) {
|
||||
const { user } = await User.getFromRequest(req);
|
||||
|
||||
if (!user) {
|
||||
if (!auth.user) {
|
||||
return new Response(undefined, {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
|
|
@ -101,7 +100,7 @@ Bun.serve({
|
|||
}
|
||||
}
|
||||
|
||||
return file.default(req, matchedRoute);
|
||||
return file.default(req, matchedRoute, auth);
|
||||
} else {
|
||||
return new Response(undefined, {
|
||||
status: 404,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@
|
|||
"start": "bun run index.ts"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"sharp"
|
||||
"sharp",
|
||||
"@prisma/client"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@julr/unocss-preset-forms": "^0.0.5",
|
||||
|
|
@ -61,6 +62,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.429.0",
|
||||
"@prisma/client": "^5.5.2",
|
||||
"chalk": "^5.3.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"ip-matching": "^2.1.2",
|
||||
|
|
@ -68,6 +70,7 @@
|
|||
"jsonld": "^8.3.1",
|
||||
"marked": "^9.1.2",
|
||||
"pg": "^8.11.3",
|
||||
"prisma": "^5.5.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"typeorm": "^0.3.17"
|
||||
}
|
||||
|
|
|
|||
166
prisma/schema.prisma
Normal file
166
prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
extensions = [pg_uuidv7]
|
||||
}
|
||||
|
||||
model Application {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
name String
|
||||
website String?
|
||||
vapid_key String?
|
||||
client_id String
|
||||
secret String
|
||||
scopes String
|
||||
redirect_uris String
|
||||
statuses Status[] // One to many relation with Status
|
||||
tokens Token[] // One to many relation with Token
|
||||
}
|
||||
|
||||
model Emoji {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
shortcode String
|
||||
url String
|
||||
visible_in_picker Boolean
|
||||
instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||
instanceId String? @db.Uuid
|
||||
alt String?
|
||||
content_type String
|
||||
users User[] // Many to many relation with User
|
||||
statuses Status[] // Many to many relation with Status
|
||||
}
|
||||
|
||||
model Instance {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
base_url String
|
||||
name String
|
||||
version String
|
||||
logo Json
|
||||
emojis Emoji[] // One to many relation with Emoji
|
||||
statuses Status[] // One to many relation with Status
|
||||
users User[] // One to many relation with User
|
||||
}
|
||||
|
||||
model Like {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
liker User @relation("UserLiked", fields: [likerId], references: [id], onDelete: Cascade)
|
||||
likerId String @db.Uuid
|
||||
liked Status @relation("LikedToStatus", fields: [likedId], references: [id], onDelete: Cascade)
|
||||
likedId String @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model LysandObject {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
remote_id String @unique
|
||||
type String
|
||||
uri String @unique
|
||||
created_at DateTime @default(now())
|
||||
author LysandObject? @relation("LysandObjectToAuthor", fields: [authorId], references: [id], onDelete: Cascade)
|
||||
authorId String? @db.Uuid
|
||||
extra_data Json
|
||||
extensions Json
|
||||
children LysandObject[] @relation("LysandObjectToAuthor")
|
||||
}
|
||||
|
||||
model Relationship {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
owner User @relation("OwnerToRelationship", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
ownerId String @db.Uuid
|
||||
subject User @relation("SubjectToRelationship", fields: [subjectId], references: [id], onDelete: Cascade)
|
||||
subjectId String @db.Uuid
|
||||
following Boolean
|
||||
showingReblogs Boolean
|
||||
notifying Boolean
|
||||
followedBy Boolean
|
||||
blocking Boolean
|
||||
blockedBy Boolean
|
||||
muting Boolean
|
||||
mutingNotifications Boolean
|
||||
requested Boolean
|
||||
domainBlocking Boolean
|
||||
endorsed Boolean
|
||||
languages String[]
|
||||
note String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Status {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
uri String @unique
|
||||
author User @relation("UserStatuses", fields: [authorId], references: [id], onDelete: Cascade)
|
||||
authorId String @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
reblog Status? @relation("StatusToStatus", fields: [reblogId], references: [id], onDelete: Cascade)
|
||||
reblogId String? @db.Uuid
|
||||
isReblog Boolean
|
||||
content String @default("")
|
||||
contentType String @default("text/plain")
|
||||
visibility String
|
||||
inReplyToPost Status? @relation("StatusToStatusReply", fields: [inReplyToPostId], references: [id], onDelete: SetNull)
|
||||
inReplyToPostId String? @db.Uuid
|
||||
quotingPost Status? @relation("StatusToStatusQuote", fields: [quotingPostId], references: [id], onDelete: SetNull)
|
||||
quotingPostId String? @db.Uuid
|
||||
instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||
instanceId String? @db.Uuid
|
||||
sensitive Boolean
|
||||
spoilerText String @default("")
|
||||
application Application? @relation(fields: [applicationId], references: [id], onDelete: SetNull)
|
||||
applicationId String? @db.Uuid
|
||||
emojis Emoji[] @relation
|
||||
mentions User[]
|
||||
likes Like[] @relation("LikedToStatus")
|
||||
reblogs Status[] @relation("StatusToStatus")
|
||||
replies Status[] @relation("StatusToStatusReply")
|
||||
quotes Status[] @relation("StatusToStatusQuote")
|
||||
pinnedBy User[] @relation("UserPinnedNotes")
|
||||
}
|
||||
|
||||
model Token {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
token_type String
|
||||
scope String
|
||||
access_token String
|
||||
code String
|
||||
created_at DateTime @default(now())
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String? @db.Uuid
|
||||
application Application? @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
applicationId String? @db.Uuid
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
uri String @unique
|
||||
username String @unique
|
||||
displayName String
|
||||
password String? // Nullable
|
||||
email String? @unique // Nullable
|
||||
note String @default("")
|
||||
isAdmin Boolean @default(false)
|
||||
endpoints Json? // Nullable
|
||||
source Json
|
||||
avatar String
|
||||
header String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
publicKey String
|
||||
privateKey String? // Nullable
|
||||
relationships Relationship[] @relation("OwnerToRelationship") // One to many relation with Relationship
|
||||
relationshipSubjects Relationship[] @relation("SubjectToRelationship") // One to many relation with Relationship
|
||||
instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade) // Many to one relation with Instance
|
||||
instanceId String? @db.Uuid
|
||||
pinnedNotes Status[] @relation("UserPinnedNotes") // Many to many relation with Status
|
||||
emojis Emoji[] // Many to many relation with Emoji
|
||||
statuses Status[] @relation("UserStatuses") // One to many relation with Status
|
||||
tokens Token[] // One to many relation with Token
|
||||
likes Like[] @relation("UserLiked") // One to many relation with Like
|
||||
statusesMentioned Status[] // Many to many relation with Status
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { getConfig, getHost } from "@config";
|
||||
import { applyConfig } from "@api";
|
||||
import { client } from "~database/datasource";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -34,9 +34,8 @@ export default async (
|
|||
return errorResponse("User is a remote user", 404);
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await client.user.findUnique({
|
||||
where: { username: requestedUser.split("@")[0] },
|
||||
relations: userRelations
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { parseRequest } from "@request";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -26,7 +26,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ export default async (
|
|||
languages?: string[];
|
||||
}>(req);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -24,11 +24,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
let foundUser: User | null;
|
||||
let foundUser: UserAction | null;
|
||||
try {
|
||||
foundUser = await User.findOne({
|
||||
foundUser = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { parseRequest } from "@request";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -26,7 +26,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ export default async (
|
|||
duration: number;
|
||||
}>(req);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { parseRequest } from "@request";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -26,7 +26,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export default async (
|
|||
comment: string;
|
||||
}>(req);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
import { FindManyOptions } from "typeorm";
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ export default async (
|
|||
tagged?: string;
|
||||
} = matchedRoute.query;
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,11 +25,11 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIAccount } from "~types/entities/account";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export const meta = applyConfig({
|
|||
* Find familiar followers (followers of a user that you also follow)
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
// Find followers of user that you also follow
|
||||
|
||||
// Get user
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: { id },
|
||||
relations: {
|
||||
relationships: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { getConfig } from "@config";
|
|||
import { parseRequest } from "@request";
|
||||
import { jsonResponse } from "@response";
|
||||
import { tempmailDomains } from "@tempmail";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -115,7 +115,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
});
|
||||
|
||||
// Check if username is taken
|
||||
if (await User.findOne({ where: { username: body.username } }))
|
||||
if (await UserAction.findOne({ where: { username: body.username } }))
|
||||
errors.details.username.push({
|
||||
error: "ERR_TAKEN",
|
||||
description: `is already taken`,
|
||||
|
|
@ -170,7 +170,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
|
||||
// TODO: Check if locale is valid
|
||||
|
||||
await User.createNewLocal({
|
||||
await UserAction.createNewLocal({
|
||||
username: body.username ?? "",
|
||||
password: body.password ?? "",
|
||||
email: body.email ?? "",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { Relationship } from "~database/entities/Relationship";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -20,7 +20,7 @@ export const meta = applyConfig({
|
|||
* Find relationships
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user: self } = await User.getFromRequest(req);
|
||||
const { user: self } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
const relationships = (
|
||||
await Promise.all(
|
||||
ids.map(async id => {
|
||||
const user = await User.findOneBy({ id });
|
||||
const user = await UserAction.findOneBy({ id });
|
||||
if (!user) return null;
|
||||
let relationship = await self.getRelationshipToOtherUser(user);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { getConfig } from "@config";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
import { sanitize } from "isomorphic-dompurify";
|
||||
import { sanitizeHtml } from "@sanitization";
|
||||
import { uploadFile } from "~classes/media";
|
||||
import { Emoji } from "~database/entities/Emoji";
|
||||
import { EmojiAction } from "~database/entities/Emoji";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["PATCH"],
|
||||
|
|
@ -24,7 +24,7 @@ export const meta = applyConfig({
|
|||
* Patches a user
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -202,8 +202,8 @@ export default async (req: Request): Promise<Response> => {
|
|||
|
||||
// Parse emojis
|
||||
|
||||
const displaynameEmojis = await Emoji.parseEmojis(sanitizedDisplayName);
|
||||
const noteEmojis = await Emoji.parseEmojis(sanitizedNote);
|
||||
const displaynameEmojis = await EmojiAction.parseEmojis(sanitizedDisplayName);
|
||||
const noteEmojis = await EmojiAction.parseEmojis(sanitizedNote);
|
||||
|
||||
user.emojis = [...displaynameEmojis, ...noteEmojis];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -17,7 +17,7 @@ export const meta = applyConfig({
|
|||
export default async (req: Request): Promise<Response> => {
|
||||
// TODO: Add checks for disabled or not email verified accounts
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { applyConfig } from "@api";
|
|||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { randomBytes } from "crypto";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -27,7 +27,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
website: string;
|
||||
}>(req);
|
||||
|
||||
const application = new Application();
|
||||
const application = new ApplicationAction();
|
||||
|
||||
application.name = client_name || "";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { User } from "~database/entities/User";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -19,8 +19,8 @@ export const meta = applyConfig({
|
|||
* Returns OAuth2 credentials
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user, token } = await User.getFromRequest(req);
|
||||
const application = await Application.getFromToken(token);
|
||||
const { user, token } = await UserAction.getFromRequest(req);
|
||||
const application = await ApplicationAction.getFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
if (!application) return errorResponse("Unauthorized", 401);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { jsonResponse } from "@response";
|
||||
import { IsNull } from "typeorm";
|
||||
import { Emoji } from "~database/entities/Emoji";
|
||||
import { EmojiAction } from "~database/entities/Emoji";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -20,7 +20,7 @@ export const meta = applyConfig({
|
|||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export default async (): Promise<Response> => {
|
||||
const emojis = await Emoji.findBy({
|
||||
const emojis = await EmojiAction.findBy({
|
||||
instance: IsNull(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { applyConfig } from "@api";
|
|||
import { getConfig } from "@config";
|
||||
import { jsonResponse } from "@response";
|
||||
import { Status } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -24,7 +24,7 @@ export default async (): Promise<Response> => {
|
|||
const config = getConfig();
|
||||
|
||||
const statusCount = await Status.count();
|
||||
const userCount = await User.count();
|
||||
const userCount = await UserAction.count();
|
||||
|
||||
// TODO: fill in more values
|
||||
return jsonResponse({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { applyConfig } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -28,7 +28,7 @@ export default async (
|
|||
// User token + read:statuses for up to 4,096 ancestors, 4,096 descendants, unlimited depth, and private statuses.
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
let foundStatus: Status | null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { MatchedRoute } from "bun";
|
||||
import { Like } from "~database/entities/Like";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -28,7 +28,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { MatchedRoute } from "bun";
|
|||
import { FindManyOptions } from "typeorm";
|
||||
import { Like } from "~database/entities/Like";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -30,7 +30,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
let foundStatus: Status | null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { applyConfig } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -27,7 +27,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
let foundStatus: Status | null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { MatchedRoute } from "bun";
|
||||
import { FindManyOptions } from "typeorm";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -29,7 +29,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
let foundStatus: Status | null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { MatchedRoute } from "bun";
|
||||
import { Like } from "~database/entities/Like";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -28,7 +28,7 @@ export default async (
|
|||
): Promise<Response> => {
|
||||
const id = matchedRoute.params.id;
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = await UserAction.getFromRequest(req);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import { getConfig } from "@config";
|
|||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { sanitizeHtml } from "@sanitization";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { parse } from "marked";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { Status, statusRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { AuthData, UserAction } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -27,9 +28,13 @@ export const meta: APIRouteMeta = applyConfig({
|
|||
/**
|
||||
* Post new status
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const { user, token } = await User.getFromRequest(req);
|
||||
const application = await Application.getFromToken(token);
|
||||
export default async (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute,
|
||||
authData: AuthData
|
||||
): Promise<Response> => {
|
||||
const { user, token } = authData;
|
||||
const application = await ApplicationAction.getFromToken(token);
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
|
|
@ -122,7 +127,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
|
||||
// Get reply account and status if exists
|
||||
let replyStatus: Status | null = null;
|
||||
let replyUser: User | null = null;
|
||||
let replyUser: UserAction | null = null;
|
||||
|
||||
if (in_reply_to_id) {
|
||||
replyStatus = await Status.findOne({
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { FindManyOptions } from "typeorm";
|
||||
import { Status, statusAndUserRelations } from "~database/entities/Status";
|
||||
import { User } from "~database/entities/User";
|
||||
import { AuthData } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -22,7 +23,11 @@ export const meta: APIRouteMeta = applyConfig({
|
|||
/**
|
||||
* Fetch home timeline statuses
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
export default async (
|
||||
req: Request,
|
||||
matchedRoute: MatchedRoute,
|
||||
authData: AuthData
|
||||
): Promise<Response> => {
|
||||
const {
|
||||
limit = 20,
|
||||
max_id,
|
||||
|
|
@ -35,7 +40,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
limit?: number;
|
||||
}>(req);
|
||||
|
||||
const { user } = await User.getFromRequest(req);
|
||||
const { user } = authData;
|
||||
|
||||
if (limit < 1 || limit > 40) {
|
||||
return errorResponse("Limit must be between 1 and 40", 400);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { applyConfig } from "@api";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
|
|
@ -18,9 +17,29 @@ export const meta: APIRouteMeta = applyConfig({
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch public timeline statuses
|
||||
*/
|
||||
const updateQuery = async (
|
||||
id: string | undefined,
|
||||
operator: string,
|
||||
query: FindManyOptions<Status>
|
||||
) => {
|
||||
if (!id) return query;
|
||||
const post = await Status.findOneBy({ id });
|
||||
if (post) {
|
||||
query = {
|
||||
...query,
|
||||
where: {
|
||||
...query.where,
|
||||
created_at: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
...(query.where as any)?.created_at,
|
||||
[operator]: post.created_at,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return query;
|
||||
};
|
||||
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const {
|
||||
local,
|
||||
|
|
@ -59,53 +78,9 @@ export default async (req: Request): Promise<Response> => {
|
|||
relations: statusAndUserRelations,
|
||||
};
|
||||
|
||||
if (max_id) {
|
||||
const maxPost = await Status.findOneBy({ id: max_id });
|
||||
if (maxPost) {
|
||||
query = {
|
||||
...query,
|
||||
where: {
|
||||
...query.where,
|
||||
created_at: {
|
||||
...(query.where as any)?.created_at,
|
||||
$lt: maxPost.created_at,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (min_id) {
|
||||
const minPost = await Status.findOneBy({ id: min_id });
|
||||
if (minPost) {
|
||||
query = {
|
||||
...query,
|
||||
where: {
|
||||
...query.where,
|
||||
created_at: {
|
||||
...(query.where as any)?.created_at,
|
||||
$gt: minPost.created_at,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (since_id) {
|
||||
const sincePost = await Status.findOneBy({ id: since_id });
|
||||
if (sincePost) {
|
||||
query = {
|
||||
...query,
|
||||
where: {
|
||||
...query.where,
|
||||
created_at: {
|
||||
...(query.where as any)?.created_at,
|
||||
$gte: sincePost.created_at,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
query = await updateQuery(max_id, "$lt", query);
|
||||
query = await updateQuery(min_id, "$gt", query);
|
||||
query = await updateQuery(since_id, "$gte", query);
|
||||
|
||||
if (only_media) {
|
||||
// TODO: add
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { applyConfig } from "@api";
|
|||
import { errorResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { randomBytes } from "crypto";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { Token } from "~database/entities/Token";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
@ -45,7 +45,7 @@ export default async (
|
|||
return errorResponse("Missing username or password", 400);
|
||||
|
||||
// Get user
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
|
|
@ -56,7 +56,7 @@ export default async (
|
|||
return errorResponse("Invalid username or password", 401);
|
||||
|
||||
// Get application
|
||||
const application = await Application.findOneBy({
|
||||
const application = await ApplicationAction.findOneBy({
|
||||
client_id,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { getConfig } from "@config";
|
|||
import { getBestContentType } from "@content_types";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { Emoji } from "~database/entities/Emoji";
|
||||
import { EmojiAction } from "~database/entities/Emoji";
|
||||
import { LysandObject } from "~database/entities/Object";
|
||||
import { Status } from "~database/entities/Status";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
import {
|
||||
ContentFormat,
|
||||
LysandAction,
|
||||
|
|
@ -61,7 +61,7 @@ export default async (
|
|||
// Process request body
|
||||
const body = (await req.json()) as LysandPublication | LysandAction;
|
||||
|
||||
const author = await User.findOne({
|
||||
const author = await UserAction.findOne({
|
||||
where: {
|
||||
uri: body.author,
|
||||
},
|
||||
|
|
@ -145,7 +145,7 @@ export default async (
|
|||
|
||||
const content = getBestContentType(body.contents);
|
||||
|
||||
const emojis = await Emoji.parseEmojis(content?.content || "");
|
||||
const emojis = await EmojiAction.parseEmojis(content?.content || "");
|
||||
|
||||
const newStatus = await Status.createNew({
|
||||
account: author,
|
||||
|
|
@ -158,7 +158,7 @@ export default async (
|
|||
sensitive: body.is_sensitive,
|
||||
uri: body.uri,
|
||||
emojis: emojis,
|
||||
mentions: await User.parseMentions(body.mentions),
|
||||
mentions: await UserAction.parseMentions(body.mentions),
|
||||
});
|
||||
|
||||
// If there is a reply, fetch all the reply parents and add them to the database
|
||||
|
|
@ -187,7 +187,7 @@ export default async (
|
|||
|
||||
const content = getBestContentType(patch.contents);
|
||||
|
||||
const emojis = await Emoji.parseEmojis(content?.content || "");
|
||||
const emojis = await EmojiAction.parseEmojis(content?.content || "");
|
||||
|
||||
const status = await Status.findOneBy({
|
||||
id: patch.patched_id,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { applyConfig } from "@api";
|
|||
import { getConfig } from "@config";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -29,7 +29,7 @@ export default async (
|
|||
|
||||
const config = getConfig();
|
||||
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
id: uuid,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,25 +3,25 @@
|
|||
import { getConfig } from "@config";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { Emoji } from "~database/entities/Emoji";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { EmojiAction } from "~database/entities/Emoji";
|
||||
import { Token, TokenType } from "~database/entities/Token";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIEmoji } from "~types/entities/emoji";
|
||||
import { APIInstance } from "~types/entities/instance";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
let token: Token;
|
||||
let user: User;
|
||||
let user2: User;
|
||||
let user: UserAction;
|
||||
let user2: UserAction;
|
||||
|
||||
describe("API Tests", () => {
|
||||
beforeAll(async () => {
|
||||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||
|
||||
// Initialize test user
|
||||
user = await User.createNewLocal({
|
||||
user = await UserAction.createNewLocal({
|
||||
email: "test@test.com",
|
||||
username: "test",
|
||||
password: "test",
|
||||
|
|
@ -29,14 +29,14 @@ describe("API Tests", () => {
|
|||
});
|
||||
|
||||
// Initialize second test user
|
||||
user2 = await User.createNewLocal({
|
||||
user2 = await UserAction.createNewLocal({
|
||||
email: "test2@test.com",
|
||||
username: "test2",
|
||||
password: "test2",
|
||||
display_name: "",
|
||||
});
|
||||
|
||||
const app = new Application();
|
||||
const app = new ApplicationAction();
|
||||
|
||||
app.name = "Test Application";
|
||||
app.website = "https://example.com";
|
||||
|
|
@ -106,7 +106,7 @@ describe("API Tests", () => {
|
|||
|
||||
describe("GET /api/v1/custom_emojis", () => {
|
||||
beforeAll(async () => {
|
||||
const emoji = new Emoji();
|
||||
const emoji = new EmojiAction();
|
||||
|
||||
emoji.instance = null;
|
||||
emoji.url = "https://example.com/test.png";
|
||||
|
|
@ -139,7 +139,7 @@ describe("API Tests", () => {
|
|||
expect(emojis[0].url).toBe("https://example.com/test.png");
|
||||
});
|
||||
afterAll(async () => {
|
||||
await Emoji.delete({ shortcode: "test" });
|
||||
await EmojiAction.delete({ shortcode: "test" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
import { getConfig } from "@config";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { Token, TokenType } from "~database/entities/Token";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIAccount } from "~types/entities/account";
|
||||
import { APIRelationship } from "~types/entities/relationship";
|
||||
import { APIStatus } from "~types/entities/status";
|
||||
|
|
@ -13,15 +13,15 @@ import { APIStatus } from "~types/entities/status";
|
|||
const config = getConfig();
|
||||
|
||||
let token: Token;
|
||||
let user: User;
|
||||
let user2: User;
|
||||
let user: UserAction;
|
||||
let user2: UserAction;
|
||||
|
||||
describe("API Tests", () => {
|
||||
beforeAll(async () => {
|
||||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||
|
||||
// Initialize test user
|
||||
user = await User.createNewLocal({
|
||||
user = await UserAction.createNewLocal({
|
||||
email: "test@test.com",
|
||||
username: "test",
|
||||
password: "test",
|
||||
|
|
@ -29,14 +29,14 @@ describe("API Tests", () => {
|
|||
});
|
||||
|
||||
// Initialize second test user
|
||||
user2 = await User.createNewLocal({
|
||||
user2 = await UserAction.createNewLocal({
|
||||
email: "test2@test.com",
|
||||
username: "test2",
|
||||
password: "test2",
|
||||
display_name: "",
|
||||
});
|
||||
|
||||
const app = new Application();
|
||||
const app = new ApplicationAction();
|
||||
|
||||
app.name = "Test Application";
|
||||
app.website = "https://example.com";
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
import { getConfig } from "@config";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { Token, TokenType } from "~database/entities/Token";
|
||||
import { User } from "~database/entities/User";
|
||||
import { UserAction } from "~database/entities/User";
|
||||
import { APIContext } from "~types/entities/context";
|
||||
import { APIStatus } from "~types/entities/status";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
let token: Token;
|
||||
let user: User;
|
||||
let user2: User;
|
||||
let user: UserAction;
|
||||
let user2: UserAction;
|
||||
let status: APIStatus | null = null;
|
||||
let status2: APIStatus | null = null;
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ describe("API Tests", () => {
|
|||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||
|
||||
// Initialize test user
|
||||
user = await User.createNewLocal({
|
||||
user = await UserAction.createNewLocal({
|
||||
email: "test@test.com",
|
||||
username: "test",
|
||||
password: "test",
|
||||
|
|
@ -30,14 +30,14 @@ describe("API Tests", () => {
|
|||
});
|
||||
|
||||
// Initialize second test user
|
||||
user2 = await User.createNewLocal({
|
||||
user2 = await UserAction.createNewLocal({
|
||||
email: "test2@test.com",
|
||||
username: "test2",
|
||||
password: "test2",
|
||||
display_name: "",
|
||||
});
|
||||
|
||||
const app = new Application();
|
||||
const app = new ApplicationAction();
|
||||
|
||||
app.name = "Test Application";
|
||||
app.website = "https://example.com";
|
||||
|
|
@ -157,7 +157,7 @@ describe("API Tests", () => {
|
|||
expect(status2.card).toBeNull();
|
||||
expect(status2.poll).toBeNull();
|
||||
expect(status2.emojis).toEqual([]);
|
||||
expect(status2.in_reply_to_id).toEqual(status?.id);
|
||||
expect(status2.in_reply_to_id).toEqual(status?.id || null);
|
||||
expect(status2.in_reply_to_account_id).toEqual(user.id);
|
||||
});
|
||||
});
|
||||
|
|
@ -181,7 +181,7 @@ describe("API Tests", () => {
|
|||
|
||||
const statusJson = (await response.json()) as APIStatus;
|
||||
|
||||
expect(statusJson.id).toBe(status?.id);
|
||||
expect(statusJson.id).toBe(status?.id || "");
|
||||
expect(statusJson.content).toBeDefined();
|
||||
expect(statusJson.created_at).toBeDefined();
|
||||
expect(statusJson.account).toBeDefined();
|
||||
|
|
@ -231,7 +231,7 @@ describe("API Tests", () => {
|
|||
expect(context.descendants.length).toBe(1);
|
||||
|
||||
// First descendant should be status2
|
||||
expect(context.descendants[0].id).toBe(status2?.id);
|
||||
expect(context.descendants[0].id).toBe(status2?.id || "");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -322,7 +322,7 @@ describe("API Tests", () => {
|
|||
"application/json"
|
||||
);
|
||||
|
||||
const users = (await response.json()) as User[];
|
||||
const users = (await response.json()) as UserAction[];
|
||||
|
||||
expect(users.length).toBe(1);
|
||||
expect(users[0].id).toBe(user.id);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { getConfig } from "@config";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { AppDataSource } from "~database/datasource";
|
||||
import { Application } from "~database/entities/Application";
|
||||
import { ApplicationAction } from "~database/entities/Application";
|
||||
import { Token } from "~database/entities/Token";
|
||||
import { User, userRelations } from "~database/entities/User";
|
||||
import { UserAction, userRelations } from "~database/entities/User";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ beforeAll(async () => {
|
|||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||
|
||||
// Initialize test user
|
||||
await User.createNewLocal({
|
||||
await UserAction.createNewLocal({
|
||||
email: "test@test.com",
|
||||
username: "test",
|
||||
password: "test",
|
||||
|
|
@ -139,7 +139,7 @@ describe("GET /api/v1/apps/verify_credentials", () => {
|
|||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe("application/json");
|
||||
|
||||
const credentials = (await response.json()) as Partial<Application>;
|
||||
const credentials = (await response.json()) as Partial<ApplicationAction>;
|
||||
|
||||
expect(credentials.name).toBe("Test Application");
|
||||
expect(credentials.website).toBe("https://example.com");
|
||||
|
|
@ -150,7 +150,7 @@ describe("GET /api/v1/apps/verify_credentials", () => {
|
|||
|
||||
afterAll(async () => {
|
||||
// Clean up user
|
||||
const user = await User.findOne({
|
||||
const user = await UserAction.findOne({
|
||||
where: {
|
||||
username: "test",
|
||||
},
|
||||
|
|
@ -164,7 +164,7 @@ afterAll(async () => {
|
|||
},
|
||||
});
|
||||
|
||||
const applications = await Application.findBy({
|
||||
const applications = await ApplicationAction.findBy({
|
||||
client_id,
|
||||
secret: client_secret,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue