Implement WebFinger, rework TS

This commit is contained in:
Jesse Wierzbinski 2023-09-12 10:48:10 -10:00
parent 29f63dfcb7
commit c573052450
No known key found for this signature in database
GPG key ID: F9A1E418934E40B0
56 changed files with 560 additions and 239 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -0,0 +1,35 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import { APIApplication } from "~types/entities/application";
/**
* Applications from clients
*/
@Entity({
name: "applications",
})
export class Application extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column("varchar")
name!: string;
@Column("varchar", {
nullable: true,
})
website!: string | null;
@Column("varchar", {
nullable: true,
})
vapid_key!: string | null;
// eslint-disable-next-line @typescript-eslint/require-await
async toAPI(): Promise<APIApplication> {
return {
name: "",
website: null,
vapid_key: null,
}
}
}

View file

@ -1,73 +0,0 @@
import { getConfig } from "@config";
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from "typeorm";
import { Status } from "~types/entities/status";
import { DBUser } from "./DBUser";
const config = getConfig();
@Entity({
name: "statuses",
})
export class DBStatus extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ManyToOne(() => DBUser, (user) => user.id)
account!: DBUser;
@CreateDateColumn()
created_at!: Date;
@UpdateDateColumn()
updated_at!: Date;
@ManyToOne(() => DBStatus, (status) => status.id, {
nullable: true,
})
reblog?: DBStatus;
@Column("boolean")
isReblog!: boolean;
toAPI(): Status {
return {
account: this.account.toAPI(),
application: null,
bookmarked: false,
created_at: this.created_at.toISOString(),
emojis: [],
favourited: false,
favourites_count: 0,
id: this.id,
in_reply_to_account_id: null,
in_reply_to_id: null,
language: null,
media_attachments: [],
mentions: [],
muted: false,
pinned: false,
poll: null,
reblog: this.isReblog ? this.reblog?.toAPI() ?? null : null,
reblogged: false,
reblogs_count: 0,
replies_count: 0,
sensitive: false,
spoiler_text: "",
tags: [],
card: null,
content: "",
uri: `${config.http.base_url}/@${this.account.username}/${this.id}`,
url: `${config.http.base_url}/@${this.account.username}/${this.id}`,
visibility: "public",
quote: null,
};
}
}

View file

@ -0,0 +1,30 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import { APIEmoji } from "~types/entities/emoji";
@Entity({
name: "emojis",
})
export class Emoji extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column("varchar")
shortcode!: string;
@Column("varchar")
url!: string;
@Column("boolean")
visible_in_picker!: boolean;
// eslint-disable-next-line @typescript-eslint/require-await
async toAPI(): Promise<APIEmoji> {
return {
shortcode: this.shortcode,
static_url: "",
url: "",
visible_in_picker: false,
category: undefined,
}
}
}

View file

@ -0,0 +1,23 @@
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "./User";
import { Status } from "./Status";
/**
* Stores an ActivityPub Like event
*/
@Entity({
name: "favourites",
})
export class Favourite extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ManyToOne(() => User, (user) => user.id)
actor!: User;
@ManyToOne(() => Status, (status) => status.id)
object!: Status;
@Column("datetime")
published!: Date;
}

View file

@ -0,0 +1,66 @@
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { APIInstance } from "~types/entities/instance";
import { User } from "./User";
@Entity({
name: "instances",
})
export class Instance extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ManyToOne(() => User, (user) => user.id)
contact_account!: User;
@Column("jsonb", {
default: {
media_attachments: {
image_matrix_limit: 0,
image_size_limit: 0,
supported_mime_types: [],
video_frame_limit: 0,
video_matrix_limit: 0,
video_size_limit: 0,
},
polls: {
max_options: 0,
max_characters_per_option: 0,
max_expiration: 0,
min_expiration: 0,
},
statuses: {
characters_reserved_per_url: 0,
max_characters: 0,
max_media_attachments: 0,
},
},
})
configuration!: APIInstance["configuration"];
async toAPI(): Promise<APIInstance> {
return {
uri: "",
approval_required: false,
email: "",
thumbnail: "",
title: "",
version: "",
configuration: this.configuration,
contact_account: await this.contact_account.toAPI(),
description: "",
invites_enabled: false,
languages: [],
registrations: false,
rules: [],
stats: {
domain_count: 0,
status_count: 0,
user_count: 0,
},
urls: {
streaming_api: "",
},
max_toot_chars: 0,
};
}
}

View file

@ -0,0 +1,18 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import {
APObject,
} from "activitypub-types";
/**
* Stores an ActivityPub object as raw JSON-LD data
*/
@Entity({
name: "objects",
})
export class RawObject extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column("json")
data!: APObject;
}

View file

@ -0,0 +1,29 @@
import {
BaseEntity,
Column,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { User } from "./User";
import { Status } from "./Status";
/**
* Stores an ActivityPub Renote event
*/
@Entity({
name: "renotes",
})
export class Renote extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ManyToOne(() => User, (user) => user.id)
actor!: User;
@ManyToOne(() => Status, (status) => status.id)
object!: Status;
@Column("datetime")
published!: Date;
}

114
database/entities/Status.ts Normal file
View file

@ -0,0 +1,114 @@
import { getConfig } from "@config";
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from "typeorm";
import { APIStatus } from "~types/entities/status";
import { User } from "./User";
import { Application } from "./Application";
import { Emoji } from "./Emoji";
import { Favourite } from "./Favourite";
const config = getConfig();
/**
* Stores ActivityPub notes
*/
@Entity({
name: "statuses",
})
export class Status extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ManyToOne(() => User, (user) => user.id)
account!: User;
@CreateDateColumn()
created_at!: Date;
@UpdateDateColumn()
updated_at!: Date;
@ManyToOne(() => Status, (status) => status.id, {
nullable: true,
})
reblog?: Status;
@Column("boolean")
isReblog!: boolean;
@Column("varchar", {
default: "",
})
content!: string;
@Column("varchar")
visibility!: APIStatus["visibility"];
@Column("boolean")
sensitive!: boolean;
@Column("varchar", {
default: "",
})
spoiler_text!: string;
@ManyToOne(() => Application, (app) => app.id, {
nullable: true
})
application!: Application | null;
@ManyToMany(() => Emoji, (emoji) => emoji.id)
emojis!: Emoji[];
async getFavourites(): Promise<Favourite[]> {
return Favourite.find({
where: {
object: {
id: this.id
},
},
});
}
async toAPI(): Promise<APIStatus> {
return {
account: await this.account.toAPI(),
application: await this.application?.toAPI() ?? null,
bookmarked: false,
created_at: this.created_at.toISOString(),
emojis: await Promise.all(this.emojis.map(async (emoji) => await emoji.toAPI())),
favourited: false,
favourites_count: (await this.getFavourites()).length,
id: this.id,
in_reply_to_account_id: null,
in_reply_to_id: null,
language: null,
media_attachments: [],
mentions: [],
muted: false,
pinned: false,
poll: null,
reblog: this.isReblog ? await this.reblog?.toAPI() ?? null : null,
reblogged: false,
reblogs_count: 0,
replies_count: 0,
sensitive: false,
spoiler_text: "",
tags: [],
card: null,
content: "",
uri: `${config.http.base_url}/@${this.account.username}/${this.id}`,
url: `${config.http.base_url}/@${this.account.username}/${this.id}`,
visibility: "public",
quote: null,
};
}
}

View file

@ -1,13 +1,16 @@
import { getConfig, getHost } from "@config";
import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
import { Account } from "~types/entities/account";
import { APIAccount } from "~types/entities/account";
const config = getConfig();
/**
* Stores local and remote users
*/
@Entity({
name: "users",
})
export class DBUser extends BaseEntity {
export class User extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id!: string;
@ -16,8 +19,10 @@ export class DBUser extends BaseEntity {
})
username!: string;
@Column("varchar")
password!: string;
@Column("varchar", {
nullable: true,
})
password!: string | null;
@Column("varchar", {
unique: true,
@ -35,7 +40,11 @@ export class DBUser extends BaseEntity {
@UpdateDateColumn()
updated_at!: Date;
toAPI(): Account {
@Column("boolean")
isRemote!: boolean;
// eslint-disable-next-line @typescript-eslint/require-await
async toAPI(): Promise<APIAccount> {
return {
acct: `@${this.username}@${getHost()}`,
avatar: "",

View file

@ -1,4 +1,5 @@
import { getConfig } from "@config";
import "reflect-metadata";
const router = new Bun.FileSystemRouter({
style: "nextjs",

View file

@ -5,6 +5,7 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"activitypub-types": "^1.0.3",
"bun-types": "latest",
"eslint": "^8.49.0",
"typescript": "^5.2.2"
@ -13,7 +14,9 @@
"typescript": "^5.0.0"
},
"dependencies": {
"jsonld": "^8.3.1",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.3.17"
}
}

View file

@ -0,0 +1,39 @@
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { User } from "~database/entities/User";
import { getHost } from "@config";
/**
* ActivityPub WebFinger endpoint
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
// In the format acct:name@example.com
const resource = matchedRoute.query.resource;
const requestedUser = resource.split("acct:")[1];
// Check if user is a local user
if (requestedUser.split("@")[1] !== getHost()) {
return errorResponse("User is a remote user", 404);
}
const user = await User.findOneBy({ username: requestedUser.split("@")[0] });
if (!user) {
return errorResponse("User not found", 404);
}
return jsonResponse({
subject: `acct:${user.username}@${getHost()}`,
links: [
{
rel: "self",
type: "application/activity+json",
href: `${getHost()}/@${user.username}/actor`
},
]
})
};

View file

@ -0,0 +1,19 @@
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { RawObject } from "~database/entities/RawObject";
/**
* Fetch a user
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const object = await RawObject.findOneBy({
id: matchedRoute.params.id
});
if (!object) return errorResponse("Object not found", 404)
return jsonResponse(object);
};

View file

@ -1,24 +1,22 @@
import { jsonResponse } from "@response";
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { DBUser } from "~database/entities/DBUser";
import { User } from "~database/entities/User";
/**
* Fetch a user
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
const user = await DBUser.findOneBy({
const user = await User.findOneBy({
id,
});
if (!user)
return jsonResponse(
{
error: "User not found",
},
404
);
return errorResponse("User not found", 404)
return jsonResponse(user.toAPI());
};

View file

@ -1,8 +1,11 @@
import { jsonResponse } from "@response";
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { DBStatus } from "~database/entities/DBStatus";
import { DBUser } from "~database/entities/DBUser";
import { Status } from "~database/entities/Status";
import { User } from "~database/entities/User";
/**
* Fetch all statuses for a user
*/
export default async (
req: Request,
matchedRoute: MatchedRoute
@ -24,19 +27,14 @@ export default async (
tagged?: string;
} = matchedRoute.query;
const user = await DBUser.findOneBy({
const user = await User.findOneBy({
id,
});
if (!user)
return jsonResponse(
{
error: "User not found",
},
404
);
return errorResponse("User not found", 404)
const statuses = await DBStatus.find({
const statuses = await Status.find({
where: {
account: {
id: user.id,

View file

@ -18,6 +18,7 @@
"strictFunctionTypes": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"types": [
"bun-types" // add Bun global
@ -25,6 +26,11 @@
"paths": {
"@*": ["./utils/*"],
"~*": ["./*"]
}
}
},
},
"include": [
"*.ts",
"**/*.ts",
"server/api/.well-known/**/*.ts"
]
}

View file

@ -1,9 +1,9 @@
import { Emoji } from "./emoji";
import { Field } from "./field";
import { Role } from "./role";
import { Source } from "./source";
import { APIEmoji } from "./emoji";
import { APIField } from "./field";
import { APIRole } from "./role";
import { APISource } from "./source";
export interface Account {
export interface APIAccount {
id: string;
username: string;
acct: string;
@ -24,11 +24,11 @@ export interface Account {
avatar_static: string;
header: string;
header_static: string;
emojis: Emoji[];
moved: Account | null;
fields: Field[];
emojis: APIEmoji[];
moved: APIAccount | null;
fields: APIField[];
bot: boolean;
source?: Source;
role?: Role;
source?: APISource;
role?: APIRole;
mute_expires_at?: string;
}

View file

@ -1,4 +1,4 @@
export interface Activity {
export interface APIActivity {
week: string;
statuses: string;
logins: string;

View file

@ -1,7 +1,7 @@
import { Emoji } from "./emoji";
import { StatusTag } from "./status";
import { APIEmoji } from "./emoji";
import { APIStatusTag } from "./status";
export interface Announcement {
export interface APIAnnouncement {
id: string;
content: string;
starts_at: string | null;
@ -13,8 +13,8 @@ export interface Announcement {
read: boolean | null;
mentions: AnnouncementAccount[];
statuses: AnnouncementStatus[];
tags: StatusTag[];
emojis: Emoji[];
tags: APIStatusTag[];
emojis: APIEmoji[];
reactions: AnnouncementReaction[];
}

View file

@ -1,4 +1,4 @@
export interface Application {
export interface APIApplication {
name: string;
website?: string | null;
vapid_key?: string | null;

View file

@ -1,13 +1,13 @@
import { Meta } from "./attachment";
import { APIMeta } from "./attachment";
export interface AsyncAttachment {
export interface APIAsyncAttachment {
id: string;
type: "unknown" | "image" | "gifv" | "video" | "audio";
url: string | null;
remote_url: string | null;
preview_url: string;
text_url: string | null;
meta: Meta | null;
meta: APIMeta | null;
description: string | null;
blurhash: string | null;
}

View file

@ -1,4 +1,4 @@
export interface Sub {
export interface APISub {
// For Image, Gifv, and Video
width?: number;
height?: number;
@ -13,15 +13,15 @@ export interface Sub {
bitrate?: number;
}
export interface Focus {
export interface APIFocus {
x: number;
y: number;
}
export interface Meta {
original?: Sub;
small?: Sub;
focus?: Focus;
export interface APIMeta {
original?: APISub;
small?: APISub;
focus?: APIFocus;
length?: string;
duration?: number;
fps?: number;
@ -34,14 +34,14 @@ export interface Meta {
audio_channel?: string;
}
export interface Attachment {
export interface APIAttachment {
id: string;
type: "unknown" | "image" | "gifv" | "video" | "audio";
url: string;
remote_url: string | null;
preview_url: string | null;
text_url: string | null;
meta: Meta | null;
meta: APIMeta | null;
description: string | null;
blurhash: string | null;
}

View file

@ -1,4 +1,4 @@
export interface Card {
export interface APICard {
url: string;
title: string;
description: string;

View file

@ -1,6 +1,6 @@
import { Status } from "./status";
import { APIStatus } from "./status";
export interface Context {
ancestors: Status[];
descendants: Status[];
export interface APIContext {
ancestors: APIStatus[];
descendants: APIStatus[];
}

View file

@ -1,9 +1,9 @@
import { Account } from "./account";
import { Status } from "./status";
import { APIAccount } from "./account";
import { APIStatus } from "./status";
export interface Conversation {
export interface APIConversation {
id: string;
accounts: Account[];
last_status: Status | null;
accounts: APIAccount[];
last_status: APIStatus | null;
unread: boolean;
}

View file

@ -1,4 +1,4 @@
export interface Emoji {
export interface APIEmoji {
shortcode: string;
static_url: string;
url: string;

View file

@ -1,4 +1,4 @@
export interface FeaturedTag {
export interface APIFeaturedTag {
id: string;
name: string;
statuses_count: number;

View file

@ -1,4 +1,4 @@
export interface Field {
export interface APIField {
name: string;
value: string;
verified_at: string | null;

View file

@ -1,4 +1,4 @@
export interface Filter {
export interface APIFilter {
id: string;
phrase: string;
context: FilterContext[];

View file

@ -1,4 +1,4 @@
export interface History {
export interface APIHistory {
day: string;
uses: number;
accounts: number;

View file

@ -1,4 +1,4 @@
export interface IdentityProof {
export interface APIIdentityProof {
provider: string;
provider_username: string;
updated_at: string;

View file

@ -1,16 +1,16 @@
import { Account } from "./account";
import { Stats } from "./stats";
import { URLs } from "./urls";
import { APIAccount } from "./account";
import { APIStats } from "./stats";
import { APIURLs } from "./urls";
export interface Instance {
export interface APIInstance {
uri: string;
title: string;
description: string;
email: string;
version: string;
thumbnail: string | null;
urls: URLs;
stats: Stats;
urls: APIURLs;
stats: APIStats;
languages: string[];
registrations: boolean;
approval_required: boolean;
@ -37,11 +37,11 @@ export interface Instance {
max_expiration: number;
};
};
contact_account: Account;
rules: InstanceRule[];
contact_account: APIAccount;
rules: APIInstanceRule[];
}
export interface InstanceRule {
export interface APIInstanceRule {
id: string;
text: string;
}

View file

@ -1,7 +1,7 @@
export interface List {
export interface APIList {
id: string;
title: string;
replies_policy: RepliesPolicy;
replies_policy: APIRepliesPolicy;
}
export type RepliesPolicy = "followed" | "list" | "none";
export type APIRepliesPolicy = "followed" | "list" | "none";

View file

@ -1,4 +1,4 @@
export interface Marker {
export interface APIMarker {
home: {
last_read_id: string;
version: number;

View file

@ -1,4 +1,4 @@
export interface Mention {
export interface APIMention {
id: string;
username: string;
url: string;

View file

@ -1,12 +1,12 @@
import { Account } from "./account";
import { Status } from "./status";
import { APIAccount } from "./account";
import { APIStatus } from "./status";
export interface Notification {
account: Account;
export interface APINotification {
account: APIAccount;
created_at: string;
id: string;
status?: Status;
type: NotificationType;
status?: APIStatus;
type: APINotificationType;
}
export type NotificationType = string;
export type APINotificationType = string;

View file

@ -1,11 +1,11 @@
import { PollOption } from "./poll_option";
import { APIPollOption } from "./poll_option";
export interface Poll {
export interface APIPoll {
id: string;
expires_at: string | null;
expired: boolean;
multiple: boolean;
votes_count: number;
options: PollOption[];
options: APIPollOption[];
voted: boolean;
}

View file

@ -1,4 +1,4 @@
export interface PollOption {
export interface APIPollOption {
title: string;
votes_count: number | null;
}

View file

@ -1,4 +1,4 @@
export interface Preferences {
export interface APIPreferences {
"posting:default:visibility": "public" | "unlisted" | "private" | "direct";
"posting:default:sensitive": boolean;
"posting:default:language": string | null;

View file

@ -1,4 +1,4 @@
export interface Alerts {
export interface APIAlerts {
follow: boolean;
favourite: boolean;
mention: boolean;
@ -6,9 +6,9 @@ export interface Alerts {
poll: boolean;
}
export interface PushSubscription {
export interface APIPushSubscription {
id: string;
endpoint: string;
server_key: string;
alerts: Alerts;
alerts: APIAlerts;
}

View file

@ -1,4 +1,4 @@
export interface Relationship {
export interface APIRelationship {
id: string;
following: boolean;
followed_by: boolean;

View file

@ -1,15 +1,15 @@
import { Account } from "./account";
import { APIAccount } from "./account";
export interface Report {
export interface APIReport {
id: string;
action_taken: boolean;
action_taken_at: string | null;
category: Category;
category: APICategory;
comment: string;
forwarded: boolean;
status_ids: string[] | null;
rule_ids: string[] | null;
target_account: Account;
target_account: APIAccount;
}
export type Category = "spam" | "violation" | "other";
export type APICategory = "spam" | "violation" | "other";

View file

@ -1,9 +1,9 @@
import { Account } from "./account";
import { Status } from "./status";
import { Tag } from "./tag";
import { APIAccount } from "./account";
import { APIStatus } from "./status";
import { APITag } from "./tag";
export interface Results {
accounts: Account[];
statuses: Status[];
hashtags: Tag[];
export interface APIResults {
accounts: APIAccount[];
statuses: APIStatus[];
hashtags: APITag[];
}

View file

@ -1,3 +1,3 @@
export interface Role {
export interface APIRole {
name: string;
}

View file

@ -1,9 +1,9 @@
import { Attachment } from "./attachment";
import { StatusParams } from "./status_params";
import { APIAttachment } from "./attachment";
import { APIStatusParams } from "./status_params";
export interface ScheduledStatus {
export interface APIScheduledStatus {
id: string;
scheduled_at: string;
params: StatusParams;
media_attachments: Attachment[];
params: APIStatusParams;
media_attachments: APIAttachment[];
}

View file

@ -1,9 +1,9 @@
import { Field } from "./field";
import { APIField } from "./field";
export interface Source {
export interface APISource {
privacy: string | null;
sensitive: boolean | null;
language: string | null;
note: string;
fields: Field[];
fields: APIField[];
}

View file

@ -1,4 +1,4 @@
export interface Stats {
export interface APIStats {
user_count: number;
status_count: number;
domain_count: number;

View file

@ -1,22 +1,22 @@
import { Account } from "./account";
import { Application } from "./application";
import { Attachment } from "./attachment";
import { Card } from "./card";
import { Emoji } from "./emoji";
import { Mention } from "./mention";
import { Poll } from "./poll";
import { APIAccount } from "./account";
import { APIApplication } from "./application";
import { APIAttachment } from "./attachment";
import { APICard } from "./card";
import { APIEmoji } from "./emoji";
import { APIMention } from "./mention";
import { APIPoll } from "./poll";
export interface Status {
export interface APIStatus {
id: string;
uri: string;
url: string;
account: Account;
account: APIAccount;
in_reply_to_id: string | null;
in_reply_to_account_id: string | null;
reblog: Status | null;
reblog: APIStatus | null;
content: string;
created_at: string;
emojis: Emoji[];
emojis: APIEmoji[];
replies_count: number;
reblogs_count: number;
favourites_count: number;
@ -26,21 +26,21 @@ export interface Status {
sensitive: boolean;
spoiler_text: string;
visibility: "public" | "unlisted" | "private" | "direct";
media_attachments: Attachment[];
mentions: Mention[];
tags: StatusTag[];
card: Card | null;
poll: Poll | null;
application: Application | null;
media_attachments: APIAttachment[];
mentions: APIMention[];
tags: APIStatusTag[];
card: APICard | null;
poll: APIPoll | null;
application: APIApplication | null;
language: string | null;
pinned: boolean | null;
bookmarked?: boolean;
// These parameters are unique parameters in fedibird.com for quote.
quote_id?: string;
quote?: Status | null;
quote?: APIStatus | null;
}
export interface StatusTag {
export interface APIStatusTag {
name: string;
url: string;
}

View file

@ -1,4 +1,4 @@
export interface StatusParams {
export interface APIStatusParams {
text: string;
in_reply_to_id: string | null;
media_ids: string[] | null;

View file

@ -1,4 +1,4 @@
export interface StatusSource {
export interface APIStatusSource {
id: string;
text: string;
spoiler_text: string;

View file

@ -1,8 +1,8 @@
import { History } from "./history";
import { APIHistory } from "./history";
export interface Tag {
export interface APITag {
name: string;
url: string;
history: History[];
history: APIHistory[];
following?: boolean;
}

View file

@ -1,4 +1,4 @@
export interface Token {
export interface APIToken {
access_token: string;
token_type: string;
scope: string;

View file

@ -1,3 +1,3 @@
export interface URLs {
export interface APIURLs {
streaming_api: string;
}

0
types/entity.ts Normal file
View file

View file

@ -6,3 +6,9 @@ export const jsonResponse = (data: object, status = 200) => {
status,
});
}
export const errorResponse = (error: string, status = 500) => {
return jsonResponse({
error: error
}, status);
}