mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
More work on converting old Prisma calls to Drizzle
This commit is contained in:
parent
66922faa51
commit
ad0bf1a350
21
biome.json
21
biome.json
|
|
@ -2,19 +2,34 @@
|
||||||
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"ignore": ["node_modules/**/*", "dist/**/*"]
|
"ignore": [
|
||||||
|
"node_modules/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"packages/frontend/.output",
|
||||||
|
"packages/frontend/.nuxt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true
|
"recommended": true
|
||||||
},
|
},
|
||||||
"ignore": ["node_modules/**/*", "dist/**/*"]
|
"ignore": [
|
||||||
|
"node_modules/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"packages/frontend/.output",
|
||||||
|
"packages/frontend/.nuxt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 4,
|
"indentWidth": 4,
|
||||||
"ignore": ["node_modules/**/*", "dist/**/*"]
|
"ignore": [
|
||||||
|
"node_modules/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"packages/frontend/.output",
|
||||||
|
"packages/frontend/.nuxt"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
cli.ts
2
cli.ts
|
|
@ -9,12 +9,12 @@ import { CliBuilder, CliCommand } from "cli-parser";
|
||||||
import Table from "cli-table";
|
import Table from "cli-table";
|
||||||
import extract from "extract-zip";
|
import extract from "extract-zip";
|
||||||
import { MediaBackend } from "media-manager";
|
import { MediaBackend } from "media-manager";
|
||||||
|
import { lookup } from "mime-types";
|
||||||
import { client } from "~database/datasource";
|
import { client } from "~database/datasource";
|
||||||
import { getUrl } from "~database/entities/Attachment";
|
import { getUrl } from "~database/entities/Attachment";
|
||||||
import { createNewLocalUser } from "~database/entities/User";
|
import { createNewLocalUser } from "~database/entities/User";
|
||||||
import { CliParameterType } from "~packages/cli-parser/cli-builder.type";
|
import { CliParameterType } from "~packages/cli-parser/cli-builder.type";
|
||||||
import { config } from "~packages/config-manager";
|
import { config } from "~packages/config-manager";
|
||||||
import { lookup } from "mime-types";
|
|
||||||
|
|
||||||
const args = process.argv;
|
const args = process.argv;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import type { Config } from "config-manager";
|
import type { Config } from "config-manager";
|
||||||
import { MediaBackendType } from "media-manager";
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
import type { APIAsyncAttachment } from "~types/entities/async_attachment";
|
|
||||||
import type { APIAttachment } from "~types/entities/attachment";
|
|
||||||
import type * as Lysand from "lysand-types";
|
import type * as Lysand from "lysand-types";
|
||||||
|
import { MediaBackendType } from "media-manager";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { attachment } from "~drizzle/schema";
|
import { attachment } from "~drizzle/schema";
|
||||||
import type { InferSelectModel } from "drizzle-orm";
|
import type { APIAsyncAttachment } from "~types/entities/async_attachment";
|
||||||
|
import type { APIAttachment } from "~types/entities/attachment";
|
||||||
|
|
||||||
export type Attachment = InferSelectModel<typeof attachment>;
|
export type Attachment = InferSelectModel<typeof attachment>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import type { APIEmoji } from "~types/entities/emoji";
|
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||||
import type * as Lysand from "lysand-types";
|
import type * as Lysand from "lysand-types";
|
||||||
import { addInstanceIfNotExists } from "./Instance";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { emoji, instance } from "~drizzle/schema";
|
import { emoji, instance } from "~drizzle/schema";
|
||||||
import { and, eq, type InferSelectModel } from "drizzle-orm";
|
import type { APIEmoji } from "~types/entities/emoji";
|
||||||
|
import { addInstanceIfNotExists } from "./Instance";
|
||||||
|
|
||||||
export type EmojiWithInstance = InferSelectModel<typeof emoji> & {
|
export type EmojiWithInstance = InferSelectModel<typeof emoji> & {
|
||||||
instance: InferSelectModel<typeof instance> | null;
|
instance: InferSelectModel<typeof instance> | null;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
import { getUserUri, type User } from "./User";
|
import type * as Lysand from "lysand-types";
|
||||||
|
import { type User, getUserUri } from "./User";
|
||||||
|
|
||||||
export const objectToInboxRequest = async (
|
export const objectToInboxRequest = async (
|
||||||
object: Lysand.Entity,
|
object: Lysand.Entity,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
import type * as Lysand from "lysand-types";
|
import type * as Lysand from "lysand-types";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
import { instance } from "~drizzle/schema";
|
import { instance } from "~drizzle/schema";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||||
|
import type * as Lysand from "lysand-types";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { like, notification } from "~drizzle/schema";
|
||||||
import type { StatusWithRelations } from "./Status";
|
import type { StatusWithRelations } from "./Status";
|
||||||
import type { UserWithRelations } from "./User";
|
import type { UserWithRelations } from "./User";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { and, eq, type InferSelectModel } from "drizzle-orm";
|
|
||||||
import { notification, like } from "~drizzle/schema";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
|
|
||||||
export type Like = InferSelectModel<typeof like>;
|
export type Like = InferSelectModel<typeof like>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import type { notification } from "~drizzle/schema";
|
||||||
import type { APINotification } from "~types/entities/notification";
|
import type { APINotification } from "~types/entities/notification";
|
||||||
import {
|
import {
|
||||||
type StatusWithRelations,
|
type StatusWithRelations,
|
||||||
statusToAPI,
|
|
||||||
findFirstStatuses,
|
findFirstStatuses,
|
||||||
|
statusToAPI,
|
||||||
} from "./Status";
|
} from "./Status";
|
||||||
import {
|
import {
|
||||||
type UserWithRelations,
|
type UserWithRelations,
|
||||||
userToAPI,
|
|
||||||
userRelations,
|
|
||||||
userExtrasTemplate,
|
|
||||||
transformOutputToUserWithRelations,
|
transformOutputToUserWithRelations,
|
||||||
|
userExtrasTemplate,
|
||||||
|
userRelations,
|
||||||
|
userToAPI,
|
||||||
} from "./User";
|
} from "./User";
|
||||||
import type { InferSelectModel } from "drizzle-orm";
|
|
||||||
import type { notification } from "~drizzle/schema";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
|
|
||||||
export type Notification = InferSelectModel<typeof notification>;
|
export type Notification = InferSelectModel<typeof notification>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { InferSelectModel } from "drizzle-orm";
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
|
import type * as Lysand from "lysand-types";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { lysandObject } from "~drizzle/schema";
|
import { lysandObject } from "~drizzle/schema";
|
||||||
import { findFirstUser } from "./User";
|
import { findFirstUser } from "./User";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
|
|
||||||
export type LysandObject = InferSelectModel<typeof lysandObject>;
|
export type LysandObject = InferSelectModel<typeof lysandObject>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { client } from "~database/datasource";
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
import type { APIRelationship } from "~types/entities/relationship";
|
import type { APIRelationship } from "~types/entities/relationship";
|
||||||
import type { User } from "./User";
|
import type { User } from "./User";
|
||||||
import type { InferSelectModel } from "drizzle-orm";
|
|
||||||
import { relationship } from "~drizzle/schema";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
|
|
||||||
export type Relationship = InferSelectModel<typeof relationship>;
|
export type Relationship = InferSelectModel<typeof relationship>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,34 @@
|
||||||
import { sanitizeHtml } from "@sanitization";
|
import { sanitizeHtml } from "@sanitization";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import {
|
||||||
|
type InferSelectModel,
|
||||||
|
and,
|
||||||
|
eq,
|
||||||
|
inArray,
|
||||||
|
isNotNull,
|
||||||
|
isNull,
|
||||||
|
or,
|
||||||
|
sql,
|
||||||
|
} from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import linkifyHtml from "linkify-html";
|
import linkifyHtml from "linkify-html";
|
||||||
import linkifyStr from "linkify-string";
|
import linkifyStr from "linkify-string";
|
||||||
|
import type * as Lysand from "lysand-types";
|
||||||
import { parse } from "marked";
|
import { parse } from "marked";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import {
|
||||||
|
type application,
|
||||||
|
attachment,
|
||||||
|
emojiToStatus,
|
||||||
|
instance,
|
||||||
|
type like,
|
||||||
|
status,
|
||||||
|
statusToUser,
|
||||||
|
user,
|
||||||
|
} from "~drizzle/schema";
|
||||||
import type { APIAttachment } from "~types/entities/attachment";
|
import type { APIAttachment } from "~types/entities/attachment";
|
||||||
import type { APIStatus } from "~types/entities/status";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
import type { Note } from "~types/lysand/Object";
|
import type { Note } from "~types/lysand/Object";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { applicationToAPI } from "./Application";
|
import { applicationToAPI } from "./Application";
|
||||||
import {
|
import {
|
||||||
attachmentFromLysand,
|
attachmentFromLysand,
|
||||||
|
|
@ -15,48 +36,27 @@ import {
|
||||||
attachmentToLysand,
|
attachmentToLysand,
|
||||||
} from "./Attachment";
|
} from "./Attachment";
|
||||||
import {
|
import {
|
||||||
|
type EmojiWithInstance,
|
||||||
emojiToAPI,
|
emojiToAPI,
|
||||||
emojiToLysand,
|
emojiToLysand,
|
||||||
fetchEmoji,
|
fetchEmoji,
|
||||||
parseEmojis,
|
parseEmojis,
|
||||||
type EmojiWithInstance,
|
|
||||||
} from "./Emoji";
|
} from "./Emoji";
|
||||||
|
import { objectToInboxRequest } from "./Federation";
|
||||||
import {
|
import {
|
||||||
getUserUri,
|
|
||||||
resolveUser,
|
|
||||||
resolveWebFinger,
|
|
||||||
userToAPI,
|
|
||||||
userExtras,
|
|
||||||
userRelations,
|
|
||||||
userExtrasTemplate,
|
|
||||||
type User,
|
type User,
|
||||||
type UserWithRelations,
|
type UserWithRelations,
|
||||||
type UserWithRelationsAndRelationships,
|
type UserWithRelationsAndRelationships,
|
||||||
transformOutputToUserWithRelations,
|
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
|
getUserUri,
|
||||||
|
resolveUser,
|
||||||
|
resolveWebFinger,
|
||||||
|
transformOutputToUserWithRelations,
|
||||||
|
userExtras,
|
||||||
|
userExtrasTemplate,
|
||||||
|
userRelations,
|
||||||
|
userToAPI,
|
||||||
} from "./User";
|
} from "./User";
|
||||||
import { objectToInboxRequest } from "./Federation";
|
|
||||||
import {
|
|
||||||
and,
|
|
||||||
eq,
|
|
||||||
or,
|
|
||||||
type InferSelectModel,
|
|
||||||
sql,
|
|
||||||
isNotNull,
|
|
||||||
inArray,
|
|
||||||
isNull,
|
|
||||||
} from "drizzle-orm";
|
|
||||||
import {
|
|
||||||
status,
|
|
||||||
type application,
|
|
||||||
attachment,
|
|
||||||
type like,
|
|
||||||
user,
|
|
||||||
statusToUser,
|
|
||||||
emojiToStatus,
|
|
||||||
instance,
|
|
||||||
} from "~drizzle/schema";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
|
|
||||||
export type Status = InferSelectModel<typeof status>;
|
export type Status = InferSelectModel<typeof status>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,10 @@
|
||||||
|
import { getBestContentType, urlToContentFormat } from "@content_types";
|
||||||
import { addUserToMeilisearch } from "@meilisearch";
|
import { addUserToMeilisearch } from "@meilisearch";
|
||||||
import { type Config, config } from "config-manager";
|
import { type Config, config } from "config-manager";
|
||||||
|
import { type InferSelectModel, and, eq, sql } from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import type { APIAccount } from "~types/entities/account";
|
|
||||||
import type { APISource } from "~types/entities/source";
|
|
||||||
import type * as Lysand from "lysand-types";
|
import type * as Lysand from "lysand-types";
|
||||||
import {
|
import { db } from "~drizzle/db";
|
||||||
fetchEmoji,
|
|
||||||
emojiToAPI,
|
|
||||||
emojiToLysand,
|
|
||||||
type EmojiWithInstance,
|
|
||||||
} from "./Emoji";
|
|
||||||
import { addInstanceIfNotExists } from "./Instance";
|
|
||||||
import { createNewRelationship } from "./Relationship";
|
|
||||||
import { getBestContentType, urlToContentFormat } from "@content_types";
|
|
||||||
import { objectToInboxRequest } from "./Federation";
|
|
||||||
import { and, eq, sql, type InferSelectModel } from "drizzle-orm";
|
|
||||||
import {
|
import {
|
||||||
emojiToUser,
|
emojiToUser,
|
||||||
instance,
|
instance,
|
||||||
|
|
@ -22,7 +12,17 @@ import {
|
||||||
relationship,
|
relationship,
|
||||||
user,
|
user,
|
||||||
} from "~drizzle/schema";
|
} from "~drizzle/schema";
|
||||||
import { db } from "~drizzle/db";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
|
import type { APISource } from "~types/entities/source";
|
||||||
|
import {
|
||||||
|
type EmojiWithInstance,
|
||||||
|
emojiToAPI,
|
||||||
|
emojiToLysand,
|
||||||
|
fetchEmoji,
|
||||||
|
} from "./Emoji";
|
||||||
|
import { objectToInboxRequest } from "./Federation";
|
||||||
|
import { addInstanceIfNotExists } from "./Instance";
|
||||||
|
import { createNewRelationship } from "./Relationship";
|
||||||
|
|
||||||
export type User = InferSelectModel<typeof user> & {
|
export type User = InferSelectModel<typeof user> & {
|
||||||
endpoints?: Partial<{
|
endpoints?: Partial<{
|
||||||
|
|
|
||||||
|
|
@ -1,462 +0,0 @@
|
||||||
-- Current sql file was generated after introspecting the database
|
|
||||||
-- If you want to run this migration please uncomment this code before executing migrations
|
|
||||||
/*
|
|
||||||
CREATE TABLE IF NOT EXISTS "_prisma_migrations" (
|
|
||||||
"id" varchar(36) PRIMARY KEY NOT NULL,
|
|
||||||
"checksum" varchar(64) NOT NULL,
|
|
||||||
"finished_at" timestamp with time zone,
|
|
||||||
"migration_name" varchar(255) NOT NULL,
|
|
||||||
"logs" text,
|
|
||||||
"rolled_back_at" timestamp with time zone,
|
|
||||||
"started_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
||||||
"applied_steps_count" integer DEFAULT 0 NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Emoji" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"shortcode" text NOT NULL,
|
|
||||||
"url" text NOT NULL,
|
|
||||||
"visible_in_picker" boolean NOT NULL,
|
|
||||||
"instanceId" uuid,
|
|
||||||
"alt" text,
|
|
||||||
"content_type" text NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Like" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"likerId" uuid NOT NULL,
|
|
||||||
"likedId" uuid NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "LysandObject" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"remote_id" text NOT NULL,
|
|
||||||
"type" text NOT NULL,
|
|
||||||
"uri" text NOT NULL,
|
|
||||||
"created_at" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"authorId" uuid,
|
|
||||||
"extra_data" jsonb NOT NULL,
|
|
||||||
"extensions" jsonb NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Relationship" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"ownerId" uuid NOT NULL,
|
|
||||||
"subjectId" uuid NOT NULL,
|
|
||||||
"following" boolean NOT NULL,
|
|
||||||
"showingReblogs" boolean NOT NULL,
|
|
||||||
"notifying" boolean NOT NULL,
|
|
||||||
"followedBy" boolean NOT NULL,
|
|
||||||
"blocking" boolean NOT NULL,
|
|
||||||
"blockedBy" boolean NOT NULL,
|
|
||||||
"muting" boolean NOT NULL,
|
|
||||||
"mutingNotifications" boolean NOT NULL,
|
|
||||||
"requested" boolean NOT NULL,
|
|
||||||
"domainBlocking" boolean NOT NULL,
|
|
||||||
"endorsed" boolean NOT NULL,
|
|
||||||
"languages" text[],
|
|
||||||
"note" text NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"updatedAt" timestamp(3) NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Application" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"name" text NOT NULL,
|
|
||||||
"website" text,
|
|
||||||
"vapid_key" text,
|
|
||||||
"client_id" text NOT NULL,
|
|
||||||
"secret" text NOT NULL,
|
|
||||||
"scopes" text NOT NULL,
|
|
||||||
"redirect_uris" text NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Token" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"token_type" text NOT NULL,
|
|
||||||
"scope" text NOT NULL,
|
|
||||||
"access_token" text NOT NULL,
|
|
||||||
"code" text NOT NULL,
|
|
||||||
"created_at" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"userId" uuid,
|
|
||||||
"applicationId" uuid
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "_EmojiToUser" (
|
|
||||||
"A" uuid NOT NULL,
|
|
||||||
"B" uuid NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "_EmojiToStatus" (
|
|
||||||
"A" uuid NOT NULL,
|
|
||||||
"B" uuid NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "_StatusToUser" (
|
|
||||||
"A" uuid NOT NULL,
|
|
||||||
"B" uuid NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "_UserPinnedNotes" (
|
|
||||||
"A" uuid NOT NULL,
|
|
||||||
"B" uuid NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Attachment" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"url" text NOT NULL,
|
|
||||||
"remote_url" text,
|
|
||||||
"thumbnail_url" text,
|
|
||||||
"mime_type" text NOT NULL,
|
|
||||||
"description" text,
|
|
||||||
"blurhash" text,
|
|
||||||
"sha256" text,
|
|
||||||
"fps" integer,
|
|
||||||
"duration" integer,
|
|
||||||
"width" integer,
|
|
||||||
"height" integer,
|
|
||||||
"size" integer,
|
|
||||||
"statusId" uuid
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Notification" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"type" text NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"notifiedId" uuid NOT NULL,
|
|
||||||
"accountId" uuid NOT NULL,
|
|
||||||
"statusId" uuid
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Status" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"uri" text,
|
|
||||||
"authorId" uuid NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"updatedAt" timestamp(3) NOT NULL,
|
|
||||||
"reblogId" uuid,
|
|
||||||
"content" text DEFAULT '' NOT NULL,
|
|
||||||
"contentType" text DEFAULT 'text/plain' NOT NULL,
|
|
||||||
"visibility" text NOT NULL,
|
|
||||||
"inReplyToPostId" uuid,
|
|
||||||
"quotingPostId" uuid,
|
|
||||||
"instanceId" uuid,
|
|
||||||
"sensitive" boolean NOT NULL,
|
|
||||||
"spoilerText" text DEFAULT '' NOT NULL,
|
|
||||||
"applicationId" uuid,
|
|
||||||
"contentSource" text DEFAULT '' NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Instance" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"base_url" text NOT NULL,
|
|
||||||
"name" text NOT NULL,
|
|
||||||
"version" text NOT NULL,
|
|
||||||
"logo" jsonb NOT NULL,
|
|
||||||
"disableAutomoderation" boolean DEFAULT false NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "OpenIdAccount" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"userId" uuid,
|
|
||||||
"serverId" text NOT NULL,
|
|
||||||
"issuerId" text NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "User" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"uri" text,
|
|
||||||
"username" text NOT NULL,
|
|
||||||
"displayName" text NOT NULL,
|
|
||||||
"password" text,
|
|
||||||
"email" text,
|
|
||||||
"note" text DEFAULT '' NOT NULL,
|
|
||||||
"isAdmin" boolean DEFAULT false NOT NULL,
|
|
||||||
"endpoints" jsonb,
|
|
||||||
"source" jsonb NOT NULL,
|
|
||||||
"avatar" text NOT NULL,
|
|
||||||
"header" text NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"updatedAt" timestamp(3) NOT NULL,
|
|
||||||
"isBot" boolean DEFAULT false NOT NULL,
|
|
||||||
"isLocked" boolean DEFAULT false NOT NULL,
|
|
||||||
"isDiscoverable" boolean DEFAULT false NOT NULL,
|
|
||||||
"sanctions" text[] DEFAULT 'RRAY[',
|
|
||||||
"publicKey" text NOT NULL,
|
|
||||||
"privateKey" text,
|
|
||||||
"instanceId" uuid,
|
|
||||||
"disableAutomoderation" boolean DEFAULT false NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "OpenIdLoginFlow" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"codeVerifier" text NOT NULL,
|
|
||||||
"applicationId" uuid,
|
|
||||||
"issuerId" text NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "Flag" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"flagType" text DEFAULT 'other' NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
"flaggeStatusId" uuid,
|
|
||||||
"flaggedUserId" uuid
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "ModNote" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"notedStatusId" uuid,
|
|
||||||
"notedUserId" uuid,
|
|
||||||
"modId" uuid NOT NULL,
|
|
||||||
"note" text NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "ModTag" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v7() NOT NULL,
|
|
||||||
"taggedStatusId" uuid,
|
|
||||||
"taggedUserId" uuid,
|
|
||||||
"modId" uuid NOT NULL,
|
|
||||||
"tag" text NOT NULL,
|
|
||||||
"createdAt" timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "LysandObject_remote_id_key" ON "LysandObject" ("remote_id");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "LysandObject_uri_key" ON "LysandObject" ("uri");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "Application_client_id_key" ON "Application" ("client_id");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "_EmojiToUser_AB_unique" ON "_EmojiToUser" ("A","B");--> statement-breakpoint
|
|
||||||
CREATE INDEX IF NOT EXISTS "_EmojiToUser_B_index" ON "_EmojiToUser" ("B");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "_EmojiToStatus_AB_unique" ON "_EmojiToStatus" ("A","B");--> statement-breakpoint
|
|
||||||
CREATE INDEX IF NOT EXISTS "_EmojiToStatus_B_index" ON "_EmojiToStatus" ("B");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "_StatusToUser_AB_unique" ON "_StatusToUser" ("A","B");--> statement-breakpoint
|
|
||||||
CREATE INDEX IF NOT EXISTS "_StatusToUser_B_index" ON "_StatusToUser" ("B");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "_UserPinnedNotes_AB_unique" ON "_UserPinnedNotes" ("A","B");--> statement-breakpoint
|
|
||||||
CREATE INDEX IF NOT EXISTS "_UserPinnedNotes_B_index" ON "_UserPinnedNotes" ("B");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "Status_uri_key" ON "Status" ("uri");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "User_uri_key" ON "User" ("uri");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "User_username_key" ON "User" ("username");--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "User_email_key" ON "User" ("email");--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Emoji" ADD CONSTRAINT "Emoji_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "public"."Instance"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Like" ADD CONSTRAINT "Like_likerId_fkey" FOREIGN KEY ("likerId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Like" ADD CONSTRAINT "Like_likedId_fkey" FOREIGN KEY ("likedId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "LysandObject" ADD CONSTRAINT "LysandObject_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."LysandObject"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Relationship" ADD CONSTRAINT "Relationship_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Relationship" ADD CONSTRAINT "Relationship_subjectId_fkey" FOREIGN KEY ("subjectId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Token" ADD CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Token" ADD CONSTRAINT "Token_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "public"."Application"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_EmojiToUser" ADD CONSTRAINT "_EmojiToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."Emoji"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_EmojiToUser" ADD CONSTRAINT "_EmojiToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_EmojiToStatus" ADD CONSTRAINT "_EmojiToStatus_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."Emoji"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_EmojiToStatus" ADD CONSTRAINT "_EmojiToStatus_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_StatusToUser" ADD CONSTRAINT "_StatusToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_StatusToUser" ADD CONSTRAINT "_StatusToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_UserPinnedNotes" ADD CONSTRAINT "_UserPinnedNotes_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "_UserPinnedNotes" ADD CONSTRAINT "_UserPinnedNotes_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Attachment" ADD CONSTRAINT "Attachment_statusId_fkey" FOREIGN KEY ("statusId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_notifiedId_fkey" FOREIGN KEY ("notifiedId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_statusId_fkey" FOREIGN KEY ("statusId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_reblogId_fkey" FOREIGN KEY ("reblogId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_inReplyToPostId_fkey" FOREIGN KEY ("inReplyToPostId") REFERENCES "public"."Status"("id") ON DELETE set null ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_quotingPostId_fkey" FOREIGN KEY ("quotingPostId") REFERENCES "public"."Status"("id") ON DELETE set null ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "public"."Instance"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Status" ADD CONSTRAINT "Status_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "public"."Application"("id") ON DELETE set null ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "OpenIdAccount" ADD CONSTRAINT "OpenIdAccount_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE set null ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "User" ADD CONSTRAINT "User_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "public"."Instance"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "OpenIdLoginFlow" ADD CONSTRAINT "OpenIdLoginFlow_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "public"."Application"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Flag" ADD CONSTRAINT "Flag_flaggeStatusId_fkey" FOREIGN KEY ("flaggeStatusId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "Flag" ADD CONSTRAINT "Flag_flaggedUserId_fkey" FOREIGN KEY ("flaggedUserId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModNote" ADD CONSTRAINT "ModNote_notedStatusId_fkey" FOREIGN KEY ("notedStatusId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModNote" ADD CONSTRAINT "ModNote_notedUserId_fkey" FOREIGN KEY ("notedUserId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModNote" ADD CONSTRAINT "ModNote_modId_fkey" FOREIGN KEY ("modId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModTag" ADD CONSTRAINT "ModTag_taggedStatusId_fkey" FOREIGN KEY ("taggedStatusId") REFERENCES "public"."Status"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModTag" ADD CONSTRAINT "ModTag_taggedUserId_fkey" FOREIGN KEY ("taggedUserId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "ModTag" ADD CONSTRAINT "ModTag_modId_fkey" FOREIGN KEY ("modId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE cascade;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
*/
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"version": "5",
|
|
||||||
"dialect": "pg",
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"idx": 0,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1712812153499,
|
|
||||||
"tag": "0000_third_misty_knight",
|
|
||||||
"breakpoints": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,292 +0,0 @@
|
||||||
import { pgTable, varchar, timestamp, text, integer, foreignKey, uuid, boolean, uniqueIndex, jsonb, index } from "drizzle-orm/pg-core"
|
|
||||||
import { sql } from "drizzle-orm"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const prismaMigrations = pgTable("_prisma_migrations", {
|
|
||||||
id: varchar("id", { length: 36 }).primaryKey().notNull(),
|
|
||||||
checksum: varchar("checksum", { length: 64 }).notNull(),
|
|
||||||
finishedAt: timestamp("finished_at", { withTimezone: true, mode: 'string' }),
|
|
||||||
migrationName: varchar("migration_name", { length: 255 }).notNull(),
|
|
||||||
logs: text("logs"),
|
|
||||||
rolledBackAt: timestamp("rolled_back_at", { withTimezone: true, mode: 'string' }),
|
|
||||||
startedAt: timestamp("started_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
appliedStepsCount: integer("applied_steps_count").default(0).notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const emoji = pgTable("Emoji", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
shortcode: text("shortcode").notNull(),
|
|
||||||
url: text("url").notNull(),
|
|
||||||
visibleInPicker: boolean("visible_in_picker").notNull(),
|
|
||||||
instanceId: uuid("instanceId").references(() => instance.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
alt: text("alt"),
|
|
||||||
contentType: text("content_type").notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const like = pgTable("Like", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
likerId: uuid("likerId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
likedId: uuid("likedId").notNull().references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const lysandObject = pgTable("LysandObject", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
remoteId: text("remote_id").notNull(),
|
|
||||||
type: text("type").notNull(),
|
|
||||||
uri: text("uri").notNull(),
|
|
||||||
createdAt: timestamp("created_at", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
authorId: uuid("authorId"),
|
|
||||||
extraData: jsonb("extra_data").notNull(),
|
|
||||||
extensions: jsonb("extensions").notNull(),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
remoteIdKey: uniqueIndex("LysandObject_remote_id_key").on(table.remoteId),
|
|
||||||
uriKey: uniqueIndex("LysandObject_uri_key").on(table.uri),
|
|
||||||
lysandObjectAuthorIdFkey: foreignKey({
|
|
||||||
columns: [table.authorId],
|
|
||||||
foreignColumns: [table.id],
|
|
||||||
name: "LysandObject_authorId_fkey"
|
|
||||||
}).onUpdate("cascade").onDelete("cascade"),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const relationship = pgTable("Relationship", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
ownerId: uuid("ownerId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
subjectId: uuid("subjectId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
following: boolean("following").notNull(),
|
|
||||||
showingReblogs: boolean("showingReblogs").notNull(),
|
|
||||||
notifying: boolean("notifying").notNull(),
|
|
||||||
followedBy: boolean("followedBy").notNull(),
|
|
||||||
blocking: boolean("blocking").notNull(),
|
|
||||||
blockedBy: boolean("blockedBy").notNull(),
|
|
||||||
muting: boolean("muting").notNull(),
|
|
||||||
mutingNotifications: boolean("mutingNotifications").notNull(),
|
|
||||||
requested: boolean("requested").notNull(),
|
|
||||||
domainBlocking: boolean("domainBlocking").notNull(),
|
|
||||||
endorsed: boolean("endorsed").notNull(),
|
|
||||||
languages: text("languages").array(),
|
|
||||||
note: text("note").notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
updatedAt: timestamp("updatedAt", { precision: 3, mode: 'string' }).notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const application = pgTable("Application", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
name: text("name").notNull(),
|
|
||||||
website: text("website"),
|
|
||||||
vapidKey: text("vapid_key"),
|
|
||||||
clientId: text("client_id").notNull(),
|
|
||||||
secret: text("secret").notNull(),
|
|
||||||
scopes: text("scopes").notNull(),
|
|
||||||
redirectUris: text("redirect_uris").notNull(),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
clientIdKey: uniqueIndex("Application_client_id_key").on(table.clientId),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const token = pgTable("Token", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
tokenType: text("token_type").notNull(),
|
|
||||||
scope: text("scope").notNull(),
|
|
||||||
accessToken: text("access_token").notNull(),
|
|
||||||
code: text("code").notNull(),
|
|
||||||
createdAt: timestamp("created_at", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
userId: uuid("userId").references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
applicationId: uuid("applicationId").references(() => application.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const emojiToUser = pgTable("_EmojiToUser", {
|
|
||||||
a: uuid("A").notNull().references(() => emoji.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
b: uuid("B").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
abUnique: uniqueIndex("_EmojiToUser_AB_unique").on(table.a, table.b),
|
|
||||||
bIdx: index().on(table.b),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const emojiToStatus = pgTable("_EmojiToStatus", {
|
|
||||||
a: uuid("A").notNull().references(() => emoji.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
b: uuid("B").notNull().references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
abUnique: uniqueIndex("_EmojiToStatus_AB_unique").on(table.a, table.b),
|
|
||||||
bIdx: index().on(table.b),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const statusToUser = pgTable("_StatusToUser", {
|
|
||||||
a: uuid("A").notNull().references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
b: uuid("B").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
abUnique: uniqueIndex("_StatusToUser_AB_unique").on(table.a, table.b),
|
|
||||||
bIdx: index().on(table.b),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const userPinnedNotes = pgTable("_UserPinnedNotes", {
|
|
||||||
a: uuid("A").notNull().references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
b: uuid("B").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
abUnique: uniqueIndex("_UserPinnedNotes_AB_unique").on(table.a, table.b),
|
|
||||||
bIdx: index().on(table.b),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const attachment = pgTable("Attachment", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
url: text("url").notNull(),
|
|
||||||
remoteUrl: text("remote_url"),
|
|
||||||
thumbnailUrl: text("thumbnail_url"),
|
|
||||||
mimeType: text("mime_type").notNull(),
|
|
||||||
description: text("description"),
|
|
||||||
blurhash: text("blurhash"),
|
|
||||||
sha256: text("sha256"),
|
|
||||||
fps: integer("fps"),
|
|
||||||
duration: integer("duration"),
|
|
||||||
width: integer("width"),
|
|
||||||
height: integer("height"),
|
|
||||||
size: integer("size"),
|
|
||||||
statusId: uuid("statusId").references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const notification = pgTable("Notification", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
type: text("type").notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
notifiedId: uuid("notifiedId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
accountId: uuid("accountId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
statusId: uuid("statusId").references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const status = pgTable("Status", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
uri: text("uri"),
|
|
||||||
authorId: uuid("authorId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
updatedAt: timestamp("updatedAt", { precision: 3, mode: 'string' }).notNull(),
|
|
||||||
reblogId: uuid("reblogId"),
|
|
||||||
content: text("content").default('').notNull(),
|
|
||||||
contentType: text("contentType").default('text/plain').notNull(),
|
|
||||||
visibility: text("visibility").notNull(),
|
|
||||||
inReplyToPostId: uuid("inReplyToPostId"),
|
|
||||||
quotingPostId: uuid("quotingPostId"),
|
|
||||||
instanceId: uuid("instanceId").references(() => instance.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
sensitive: boolean("sensitive").notNull(),
|
|
||||||
spoilerText: text("spoilerText").default('').notNull(),
|
|
||||||
applicationId: uuid("applicationId").references(() => application.id, { onDelete: "set null", onUpdate: "cascade" } ),
|
|
||||||
contentSource: text("contentSource").default('').notNull(),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
uriKey: uniqueIndex("Status_uri_key").on(table.uri),
|
|
||||||
statusReblogIdFkey: foreignKey({
|
|
||||||
columns: [table.reblogId],
|
|
||||||
foreignColumns: [table.id],
|
|
||||||
name: "Status_reblogId_fkey"
|
|
||||||
}).onUpdate("cascade").onDelete("cascade"),
|
|
||||||
statusInReplyToPostIdFkey: foreignKey({
|
|
||||||
columns: [table.inReplyToPostId],
|
|
||||||
foreignColumns: [table.id],
|
|
||||||
name: "Status_inReplyToPostId_fkey"
|
|
||||||
}).onUpdate("cascade").onDelete("set null"),
|
|
||||||
statusQuotingPostIdFkey: foreignKey({
|
|
||||||
columns: [table.quotingPostId],
|
|
||||||
foreignColumns: [table.id],
|
|
||||||
name: "Status_quotingPostId_fkey"
|
|
||||||
}).onUpdate("cascade").onDelete("set null"),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const instance = pgTable("Instance", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
baseUrl: text("base_url").notNull(),
|
|
||||||
name: text("name").notNull(),
|
|
||||||
version: text("version").notNull(),
|
|
||||||
logo: jsonb("logo").notNull(),
|
|
||||||
disableAutomoderation: boolean("disableAutomoderation").default(false).notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const openIdAccount = pgTable("OpenIdAccount", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
userId: uuid("userId").references(() => user.id, { onDelete: "set null", onUpdate: "cascade" } ),
|
|
||||||
serverId: text("serverId").notNull(),
|
|
||||||
issuerId: text("issuerId").notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const user = pgTable("User", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
uri: text("uri"),
|
|
||||||
username: text("username").notNull(),
|
|
||||||
displayName: text("displayName").notNull(),
|
|
||||||
password: text("password"),
|
|
||||||
email: text("email"),
|
|
||||||
note: text("note").default('').notNull(),
|
|
||||||
isAdmin: boolean("isAdmin").default(false).notNull(),
|
|
||||||
endpoints: jsonb("endpoints"),
|
|
||||||
source: jsonb("source").notNull(),
|
|
||||||
avatar: text("avatar").notNull(),
|
|
||||||
header: text("header").notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
updatedAt: timestamp("updatedAt", { precision: 3, mode: 'string' }).notNull(),
|
|
||||||
isBot: boolean("isBot").default(false).notNull(),
|
|
||||||
isLocked: boolean("isLocked").default(false).notNull(),
|
|
||||||
isDiscoverable: boolean("isDiscoverable").default(false).notNull(),
|
|
||||||
sanctions: text("sanctions").default('RRAY[').array(),
|
|
||||||
publicKey: text("publicKey").notNull(),
|
|
||||||
privateKey: text("privateKey"),
|
|
||||||
instanceId: uuid("instanceId").references(() => instance.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
disableAutomoderation: boolean("disableAutomoderation").default(false).notNull(),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
uriKey: uniqueIndex("User_uri_key").on(table.uri),
|
|
||||||
usernameKey: uniqueIndex("User_username_key").on(table.username),
|
|
||||||
emailKey: uniqueIndex("User_email_key").on(table.email),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const openIdLoginFlow = pgTable("OpenIdLoginFlow", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
codeVerifier: text("codeVerifier").notNull(),
|
|
||||||
applicationId: uuid("applicationId").references(() => application.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
issuerId: text("issuerId").notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const flag = pgTable("Flag", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
flagType: text("flagType").default('other').notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
flaggeStatusId: uuid("flaggeStatusId").references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
flaggedUserId: uuid("flaggedUserId").references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const modNote = pgTable("ModNote", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
notedStatusId: uuid("notedStatusId").references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
notedUserId: uuid("notedUserId").references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
modId: uuid("modId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
note: text("note").notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const modTag = pgTable("ModTag", {
|
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
||||||
taggedStatusId: uuid("taggedStatusId").references(() => status.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
taggedUserId: uuid("taggedUserId").references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
modId: uuid("modId").notNull().references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" } ),
|
|
||||||
tag: text("tag").notNull(),
|
|
||||||
createdAt: timestamp("createdAt", { precision: 3, mode: 'string' }).defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Config } from "drizzle-kit";
|
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import type { Config } from "drizzle-kit";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
driver: "pg",
|
driver: "pg",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Client } from "pg";
|
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
import { drizzle } from "drizzle-orm/node-postgres";
|
import { drizzle } from "drizzle-orm/node-postgres";
|
||||||
|
import { Client } from "pg";
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
|
|
||||||
export const client = new Client({
|
export const client = new Client({
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"dialect": "pg",
|
"dialect": "pg",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"when": 1712805159664,
|
"when": 1712805159664,
|
||||||
"tag": "0000_illegal_living_lightning",
|
"tag": "0000_illegal_living_lightning",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import {
|
|
||||||
pgTable,
|
|
||||||
timestamp,
|
|
||||||
text,
|
|
||||||
integer,
|
|
||||||
foreignKey,
|
|
||||||
uuid,
|
|
||||||
boolean,
|
|
||||||
uniqueIndex,
|
|
||||||
jsonb,
|
|
||||||
index,
|
|
||||||
} from "drizzle-orm/pg-core";
|
|
||||||
import { relations, sql } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
|
import {
|
||||||
|
boolean,
|
||||||
|
foreignKey,
|
||||||
|
index,
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uniqueIndex,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const emoji = pgTable("Emoji", {
|
export const emoji = pgTable("Emoji", {
|
||||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||||
|
|
|
||||||
4
index.ts
4
index.ts
|
|
@ -4,11 +4,11 @@ import { connectMeili } from "@meilisearch";
|
||||||
import { moduleIsEntry } from "@module";
|
import { moduleIsEntry } from "@module";
|
||||||
import { initializeRedisCache } from "@redis";
|
import { initializeRedisCache } from "@redis";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import { count, sql } from "drizzle-orm";
|
||||||
import { LogLevel, LogManager, MultiLogManager } from "log-manager";
|
import { LogLevel, LogManager, MultiLogManager } from "log-manager";
|
||||||
import { createServer } from "~server";
|
|
||||||
import { db, client as pgClient } from "~drizzle/db";
|
import { db, client as pgClient } from "~drizzle/db";
|
||||||
import { status } from "~drizzle/schema";
|
import { status } from "~drizzle/schema";
|
||||||
import { count, sql } from "drizzle-orm";
|
import { createServer } from "~server";
|
||||||
|
|
||||||
await pgClient.connect();
|
await pgClient.connect();
|
||||||
const timeAtStart = performance.now();
|
const timeAtStart = performance.now();
|
||||||
|
|
|
||||||
272
package.json
272
package.json
|
|
@ -1,139 +1,139 @@
|
||||||
{
|
{
|
||||||
"name": "lysand",
|
"name": "lysand",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"description": "A project to build a federated social network",
|
"description": "A project to build a federated social network",
|
||||||
"author": {
|
"author": {
|
||||||
"email": "contact@cpluspatch.com",
|
"email": "contact@cpluspatch.com",
|
||||||
"name": "CPlusPatch",
|
"name": "CPlusPatch",
|
||||||
"url": "https://cpluspatch.com"
|
"url": "https://cpluspatch.com"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/lysand-org/lysand/issues"
|
"url": "https://github.com/lysand-org/lysand/issues"
|
||||||
},
|
},
|
||||||
"icon": "https://github.com/lysand-org/lysand",
|
"icon": "https://github.com/lysand-org/lysand",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"keywords": ["federated", "activitypub", "bun"],
|
"keywords": ["federated", "activitypub", "bun"],
|
||||||
"workspaces": ["packages/*"],
|
"workspaces": ["packages/*"],
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
{
|
{
|
||||||
"email": "contact@cpluspatch.com",
|
"email": "contact@cpluspatch.com",
|
||||||
"name": "CPlusPatch",
|
"name": "CPlusPatch",
|
||||||
"url": "https://cpluspatch.com"
|
"url": "https://cpluspatch.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lysand-org/lysand.git"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun run --watch index.ts",
|
||||||
|
"vite:dev": "bunx --bun vite pages",
|
||||||
|
"vite:build": "bunx --bun vite build pages",
|
||||||
|
"fe:dev": "bun --bun nuxt dev packages/frontend",
|
||||||
|
"fe:build": "bun --bun nuxt build packages/frontend",
|
||||||
|
"fe:analyze": "bun --bun nuxt analyze packages/frontend",
|
||||||
|
"start": "NITRO_PORT=5173 bun run dist/frontend/server/index.mjs & NODE_ENV=production bun run dist/index.js --prod",
|
||||||
|
"migrate-dev": "bun prisma migrate dev",
|
||||||
|
"migrate": "bun prisma migrate deploy",
|
||||||
|
"lint": "bunx --bun eslint --config .eslintrc.cjs --ext .ts .",
|
||||||
|
"prod-build": "bun run build.ts",
|
||||||
|
"prisma": "DATABASE_URL=$(bun run prisma.ts) bunx prisma",
|
||||||
|
"generate": "bun prisma generate",
|
||||||
|
"benchmark:timeline": "bun run benchmarks/timelines.ts",
|
||||||
|
"cloc": "cloc . --exclude-dir node_modules,dist",
|
||||||
|
"cli": "bun run cli.ts"
|
||||||
|
},
|
||||||
|
"trustedDependencies": [
|
||||||
|
"@biomejs/biome",
|
||||||
|
"@fortawesome/fontawesome-common-types",
|
||||||
|
"@fortawesome/free-regular-svg-icons",
|
||||||
|
"@fortawesome/free-solid-svg-icons",
|
||||||
|
"@prisma/client",
|
||||||
|
"@prisma/engines",
|
||||||
|
"es5-ext",
|
||||||
|
"esbuild",
|
||||||
|
"json-editor-vue",
|
||||||
|
"msgpackr-extract",
|
||||||
|
"nuxt-app",
|
||||||
|
"prisma",
|
||||||
|
"sharp",
|
||||||
|
"vue-demi"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.6.4",
|
||||||
|
"@img/sharp-wasm32": "^0.33.3",
|
||||||
|
"@julr/unocss-preset-forms": "^0.1.0",
|
||||||
|
"@nuxtjs/seo": "^2.0.0-rc.10",
|
||||||
|
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||||
|
"@types/cli-table": "^0.3.4",
|
||||||
|
"@types/html-to-text": "^9.0.4",
|
||||||
|
"@types/ioredis": "^5.0.0",
|
||||||
|
"@types/jsonld": "^1.5.13",
|
||||||
|
"@types/mime-types": "^2.1.4",
|
||||||
|
"@types/pg": "^8.11.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "latest",
|
||||||
|
"@unocss/cli": "latest",
|
||||||
|
"@unocss/transformer-directives": "^0.59.0",
|
||||||
|
"@vitejs/plugin-vue": "latest",
|
||||||
|
"@vueuse/head": "^2.0.0",
|
||||||
|
"activitypub-types": "^1.0.3",
|
||||||
|
"bun-types": "latest",
|
||||||
|
"drizzle-kit": "^0.20.14",
|
||||||
|
"shiki": "^1.2.4",
|
||||||
|
"typescript": "latest",
|
||||||
|
"unocss": "latest",
|
||||||
|
"untyped": "^1.4.2",
|
||||||
|
"vite": "^5.2.8",
|
||||||
|
"vite-ssr": "^0.17.1",
|
||||||
|
"vue": "^3.3.9",
|
||||||
|
"vue-router": "^4.2.5",
|
||||||
|
"vue-tsc": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.461.0",
|
||||||
|
"@iarna/toml": "^2.2.5",
|
||||||
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
|
"@prisma/client": "^5.6.0",
|
||||||
|
"blurhash": "^2.0.5",
|
||||||
|
"bullmq": "latest",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"cli-parser": "workspace:*",
|
||||||
|
"cli-table": "^0.3.11",
|
||||||
|
"config-manager": "workspace:*",
|
||||||
|
"drizzle-orm": "^0.30.7",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
|
"extract-zip": "^2.0.1",
|
||||||
|
"html-to-text": "^9.0.5",
|
||||||
|
"ioredis": "^5.3.2",
|
||||||
|
"ip-matching": "^2.1.2",
|
||||||
|
"iso-639-1": "^3.1.0",
|
||||||
|
"isomorphic-dompurify": "latest",
|
||||||
|
"jsonld": "^8.3.1",
|
||||||
|
"linkify-html": "^4.1.3",
|
||||||
|
"linkify-string": "^4.1.3",
|
||||||
|
"linkifyjs": "^4.1.3",
|
||||||
|
"log-manager": "workspace:*",
|
||||||
|
"marked": "latest",
|
||||||
|
"media-manager": "workspace:*",
|
||||||
|
"megalodon": "^10.0.0",
|
||||||
|
"meilisearch": "latest",
|
||||||
|
"merge-deep-ts": "^1.2.6",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
|
"next-route-matcher": "^1.0.1",
|
||||||
|
"oauth4webapi": "^2.4.0",
|
||||||
|
"pg": "^8.11.5",
|
||||||
|
"prisma": "^5.6.0",
|
||||||
|
"prisma-json-types-generator": "^3.0.4",
|
||||||
|
"prisma-redis-middleware": "^4.8.0",
|
||||||
|
"request-parser": "workspace:*",
|
||||||
|
"semver": "^7.5.4",
|
||||||
|
"sharp": "^0.33.3",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/lysand-org/lysand.git"
|
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "bun run --watch index.ts",
|
|
||||||
"vite:dev": "bunx --bun vite pages",
|
|
||||||
"vite:build": "bunx --bun vite build pages",
|
|
||||||
"fe:dev": "bun --bun nuxt dev packages/frontend",
|
|
||||||
"fe:build": "bun --bun nuxt build packages/frontend",
|
|
||||||
"fe:analyze": "bun --bun nuxt analyze packages/frontend",
|
|
||||||
"start": "NITRO_PORT=5173 bun run dist/frontend/server/index.mjs & NODE_ENV=production bun run dist/index.js --prod",
|
|
||||||
"migrate-dev": "bun prisma migrate dev",
|
|
||||||
"migrate": "bun prisma migrate deploy",
|
|
||||||
"lint": "bunx --bun eslint --config .eslintrc.cjs --ext .ts .",
|
|
||||||
"prod-build": "bun run build.ts",
|
|
||||||
"prisma": "DATABASE_URL=$(bun run prisma.ts) bunx prisma",
|
|
||||||
"generate": "bun prisma generate",
|
|
||||||
"benchmark:timeline": "bun run benchmarks/timelines.ts",
|
|
||||||
"cloc": "cloc . --exclude-dir node_modules,dist",
|
|
||||||
"cli": "bun run cli.ts"
|
|
||||||
},
|
|
||||||
"trustedDependencies": [
|
|
||||||
"@biomejs/biome",
|
|
||||||
"@fortawesome/fontawesome-common-types",
|
|
||||||
"@fortawesome/free-regular-svg-icons",
|
|
||||||
"@fortawesome/free-solid-svg-icons",
|
|
||||||
"@prisma/client",
|
|
||||||
"@prisma/engines",
|
|
||||||
"es5-ext",
|
|
||||||
"esbuild",
|
|
||||||
"json-editor-vue",
|
|
||||||
"msgpackr-extract",
|
|
||||||
"nuxt-app",
|
|
||||||
"prisma",
|
|
||||||
"sharp",
|
|
||||||
"vue-demi"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "1.6.4",
|
|
||||||
"@img/sharp-wasm32": "^0.33.3",
|
|
||||||
"@julr/unocss-preset-forms": "^0.1.0",
|
|
||||||
"@nuxtjs/seo": "^2.0.0-rc.10",
|
|
||||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
|
||||||
"@types/cli-table": "^0.3.4",
|
|
||||||
"@types/html-to-text": "^9.0.4",
|
|
||||||
"@types/ioredis": "^5.0.0",
|
|
||||||
"@types/jsonld": "^1.5.13",
|
|
||||||
"@types/mime-types": "^2.1.4",
|
|
||||||
"@types/pg": "^8.11.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "latest",
|
|
||||||
"@unocss/cli": "latest",
|
|
||||||
"@unocss/transformer-directives": "^0.59.0",
|
|
||||||
"@vitejs/plugin-vue": "latest",
|
|
||||||
"@vueuse/head": "^2.0.0",
|
|
||||||
"activitypub-types": "^1.0.3",
|
|
||||||
"bun-types": "latest",
|
|
||||||
"drizzle-kit": "^0.20.14",
|
|
||||||
"shiki": "^1.2.4",
|
|
||||||
"typescript": "latest",
|
|
||||||
"unocss": "latest",
|
|
||||||
"untyped": "^1.4.2",
|
|
||||||
"vite": "^5.2.8",
|
|
||||||
"vite-ssr": "^0.17.1",
|
|
||||||
"vue": "^3.3.9",
|
|
||||||
"vue-router": "^4.2.5",
|
|
||||||
"vue-tsc": "latest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5.3.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-sdk/client-s3": "^3.461.0",
|
|
||||||
"@iarna/toml": "^2.2.5",
|
|
||||||
"@json2csv/plainjs": "^7.0.6",
|
|
||||||
"@prisma/client": "^5.6.0",
|
|
||||||
"blurhash": "^2.0.5",
|
|
||||||
"bullmq": "latest",
|
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"cli-parser": "workspace:*",
|
|
||||||
"cli-table": "^0.3.11",
|
|
||||||
"config-manager": "workspace:*",
|
|
||||||
"drizzle-orm": "^0.30.7",
|
|
||||||
"eventemitter3": "^5.0.1",
|
|
||||||
"extract-zip": "^2.0.1",
|
|
||||||
"html-to-text": "^9.0.5",
|
|
||||||
"ioredis": "^5.3.2",
|
|
||||||
"ip-matching": "^2.1.2",
|
|
||||||
"iso-639-1": "^3.1.0",
|
|
||||||
"isomorphic-dompurify": "latest",
|
|
||||||
"jsonld": "^8.3.1",
|
|
||||||
"linkify-html": "^4.1.3",
|
|
||||||
"linkify-string": "^4.1.3",
|
|
||||||
"linkifyjs": "^4.1.3",
|
|
||||||
"log-manager": "workspace:*",
|
|
||||||
"marked": "latest",
|
|
||||||
"media-manager": "workspace:*",
|
|
||||||
"megalodon": "^10.0.0",
|
|
||||||
"meilisearch": "latest",
|
|
||||||
"merge-deep-ts": "^1.2.6",
|
|
||||||
"mime-types": "^2.1.35",
|
|
||||||
"next-route-matcher": "^1.0.1",
|
|
||||||
"oauth4webapi": "^2.4.0",
|
|
||||||
"pg": "^8.11.5",
|
|
||||||
"prisma": "^5.6.0",
|
|
||||||
"prisma-json-types-generator": "^3.0.4",
|
|
||||||
"prisma-redis-middleware": "^4.8.0",
|
|
||||||
"request-parser": "workspace:*",
|
|
||||||
"semver": "^7.5.4",
|
|
||||||
"sharp": "^0.33.3",
|
|
||||||
"strip-ansi": "^7.1.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ETailwind, EButton } from "vue-email";
|
import { EButton, ETailwind } from "vue-email";
|
||||||
import tailwindConfig from "~/tailwind.config";
|
import tailwindConfig from "~/tailwind.config";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
{
|
{
|
||||||
"name": "nuxt-app",
|
"name": "nuxt-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"c12": "^1.10.0",
|
"c12": "^1.10.0",
|
||||||
"nuxt": "^3.11.2",
|
"nuxt": "^3.11.2",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-router": "^4.3.0"
|
"vue-router": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@vue-email/nuxt": "^0.8.19"
|
"@vue-email/nuxt": "^0.8.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { getHighlighterCore } from "shiki/core";
|
import { getHighlighterCore } from "shiki/core";
|
||||||
import getWasm from "shiki/wasm";
|
import getWasm from "shiki/wasm";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const config = (await useFetch("/api/config")).data.value;
|
const config = (await useFetch("/api/config")).data.value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { getHighlighterCore } from "shiki/core";
|
import { getHighlighterCore } from "shiki/core";
|
||||||
import getWasm from "shiki/wasm";
|
import getWasm from "shiki/wasm";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const config = (await useFetch("/api/config")).data.value;
|
const config = (await useFetch("/api/config")).data.value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Config } from "tailwindcss";
|
|
||||||
import forms from "@tailwindcss/forms";
|
import forms from "@tailwindcss/forms";
|
||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
// Default are on https://tailwindcss.nuxtjs.org/tailwind/config#default-configuration
|
// Default are on https://tailwindcss.nuxtjs.org/tailwind/config#default-configuration
|
||||||
export default (<Partial<Config>>{
|
export default (<Partial<Config>>{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { appendFile, mkdir, exists } from "node:fs/promises";
|
import { appendFile, exists, mkdir } from "node:fs/promises";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
import type { BunFile } from "bun";
|
import type { BunFile } from "bun";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Config } from "config-manager";
|
|
||||||
import { S3Client } from "@jsr/bradenmacdonald__s3-lite-client";
|
import { S3Client } from "@jsr/bradenmacdonald__s3-lite-client";
|
||||||
|
import type { Config } from "config-manager";
|
||||||
import type { ConvertableMediaFormats } from "./media-converter";
|
import type { ConvertableMediaFormats } from "./media-converter";
|
||||||
import { MediaConverter } from "./media-converter";
|
import { MediaConverter } from "./media-converter";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ import type { S3Client } from "@jsr/bradenmacdonald__s3-lite-client";
|
||||||
import type { Config } from "config-manager";
|
import type { Config } from "config-manager";
|
||||||
// FILEPATH: /home/jessew/Dev/lysand/packages/media-manager/backends/s3.test.ts
|
// FILEPATH: /home/jessew/Dev/lysand/packages/media-manager/backends/s3.test.ts
|
||||||
import {
|
import {
|
||||||
|
LocalMediaBackend,
|
||||||
MediaBackend,
|
MediaBackend,
|
||||||
MediaBackendType,
|
MediaBackendType,
|
||||||
MediaHasher,
|
MediaHasher,
|
||||||
LocalMediaBackend,
|
|
||||||
S3MediaBackend,
|
S3MediaBackend,
|
||||||
} from "..";
|
} from "..";
|
||||||
import { ConvertableMediaFormats, MediaConverter } from "../media-converter";
|
import { ConvertableMediaFormats, MediaConverter } from "../media-converter";
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { randomBytes } from "node:crypto";
|
import { randomBytes } from "node:crypto";
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { TokenType } from "~database/entities/Token";
|
import { TokenType } from "~database/entities/Token";
|
||||||
import { userRelations } from "~database/entities/relations";
|
import { findFirstUser } from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { token } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -47,45 +48,28 @@ export default apiRoute<{
|
||||||
if (!email || !password)
|
if (!email || !password)
|
||||||
return redirectToLogin("Invalid username or password");
|
return redirectToLogin("Invalid username or password");
|
||||||
|
|
||||||
// Get user
|
const user = await findFirstUser({
|
||||||
const user = await client.user.findFirst({
|
where: (user, { eq }) => eq(user.email, email),
|
||||||
where: {
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !(await Bun.password.verify(password, user.password || "")))
|
if (!user || !(await Bun.password.verify(password, user.password || "")))
|
||||||
return redirectToLogin("Invalid username or password");
|
return redirectToLogin("Invalid username or password");
|
||||||
|
|
||||||
// Get application
|
const application = await db.query.application.findFirst({
|
||||||
const application = await client.application.findFirst({
|
where: (app, { eq }) => eq(app.clientId, client_id),
|
||||||
where: {
|
|
||||||
client_id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!application) return redirectToLogin("Invalid client_id");
|
if (!application) return redirectToLogin("Invalid client_id");
|
||||||
|
|
||||||
const code = randomBytes(32).toString("hex");
|
const code = randomBytes(32).toString("hex");
|
||||||
|
|
||||||
await client.application.update({
|
await db.insert(token).values({
|
||||||
where: { id: application.id },
|
accessToken: randomBytes(64).toString("base64url"),
|
||||||
data: {
|
code: code,
|
||||||
tokens: {
|
scope: scopes.join(" "),
|
||||||
create: {
|
tokenType: TokenType.BEARER,
|
||||||
access_token: randomBytes(64).toString("base64url"),
|
applicationId: application.id,
|
||||||
code: code,
|
userId: user.id,
|
||||||
scope: scopes.join(" "),
|
|
||||||
token_type: TokenType.BEARER,
|
|
||||||
user: {
|
|
||||||
connect: {
|
|
||||||
id: user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Redirect to OAuth confirmation screen
|
// Redirect to OAuth confirmation screen
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { client } from "~database/datasource";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { userRelations } from "~database/entities/relations";
|
import { db } from "~drizzle/db";
|
||||||
|
import { application, token } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -34,23 +35,15 @@ export default apiRoute<{
|
||||||
302,
|
302,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get token
|
const foundToken = await db
|
||||||
const token = await client.token.findFirst({
|
.select()
|
||||||
where: {
|
.from(token)
|
||||||
code,
|
.leftJoin(application, eq(token.applicationId, application.id))
|
||||||
application: {
|
.where(and(eq(token.code, code), eq(application.clientId, client_id)))
|
||||||
client_id,
|
.limit(1);
|
||||||
},
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
user: {
|
|
||||||
include: userRelations,
|
|
||||||
},
|
|
||||||
application: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!token) return redirectToLogin("Invalid code");
|
if (!foundToken || foundToken.length <= 0)
|
||||||
|
return redirectToLogin("Invalid code");
|
||||||
|
|
||||||
// Redirect back to application
|
// Redirect back to application
|
||||||
return Response.redirect(`${redirect_uri}?code=${code}`, 302);
|
return Response.redirect(`${redirect_uri}?code=${code}`, 302);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,52 +32,24 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
|
||||||
|
|
||||||
if (!relationship) {
|
if (!foundRelationship.blocking) {
|
||||||
// Create new relationship
|
foundRelationship.blocking = true;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
|
||||||
|
|
||||||
await client.user.update({
|
|
||||||
where: { id: self.id },
|
|
||||||
data: {
|
|
||||||
relationships: {
|
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!relationship.blocking) {
|
await db
|
||||||
relationship.blocking = true;
|
.update(relationship)
|
||||||
}
|
.set({
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
blocking: true,
|
blocking: true,
|
||||||
},
|
})
|
||||||
});
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
|
||||||
createNewRelationship,
|
|
||||||
relationshipToAPI,
|
|
||||||
} from "~database/entities/Relationship";
|
|
||||||
import {
|
import {
|
||||||
|
findFirstUser,
|
||||||
followRequestUser,
|
followRequestUser,
|
||||||
getRelationshipToOtherUser,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
|
@ -39,27 +36,19 @@ export default apiRoute<{
|
||||||
|
|
||||||
const { languages, notify, reblogs } = extraData.parsedRequest;
|
const { languages, notify, reblogs } = extraData.parsedRequest;
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
let relationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship.following) {
|
if (!relationship.following) {
|
||||||
relationship = await followRequestUser(
|
relationship = await followRequestUser(
|
||||||
self,
|
self,
|
||||||
user,
|
otherUser,
|
||||||
relationship.id,
|
relationship.id,
|
||||||
reblogs,
|
reblogs,
|
||||||
notify,
|
notify,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
import {
|
||||||
import { type UserWithRelations, userToAPI } from "~database/entities/User";
|
type UserWithRelations,
|
||||||
import { userRelations } from "~database/entities/relations";
|
findFirstUser,
|
||||||
|
findManyUsers,
|
||||||
|
userToAPI,
|
||||||
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -32,36 +35,27 @@ export default apiRoute<{
|
||||||
// TODO: Add pinned
|
// TODO: Add pinned
|
||||||
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
|
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (limit < 1 || limit > 40) return errorResponse("Invalid limit", 400);
|
if (limit < 1 || limit > 40) return errorResponse("Invalid limit", 400);
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
||||||
client.user,
|
findManyUsers,
|
||||||
{
|
{
|
||||||
where: {
|
// @ts-ignore
|
||||||
relationships: {
|
where: (follower, { and, lt, gt, gte, eq, sql }) =>
|
||||||
some: {
|
and(
|
||||||
subjectId: user.id,
|
max_id ? lt(follower.id, max_id) : undefined,
|
||||||
following: true,
|
since_id ? gte(follower.id, since_id) : undefined,
|
||||||
},
|
min_id ? gt(follower.id, min_id) : undefined,
|
||||||
},
|
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${otherUser.id} AND "Relationship"."objectId" = ${follower.id} AND "Relationship"."following" = true)`,
|
||||||
id: {
|
),
|
||||||
lt: max_id,
|
// @ts-expect-error Yes I KNOW the types are wrong
|
||||||
gt: min_id,
|
orderBy: (liker, { desc }) => desc(liker.id),
|
||||||
gte: since_id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
take: Number(limit),
|
|
||||||
orderBy: {
|
|
||||||
id: "desc",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
import {
|
||||||
import { userToAPI, type UserWithRelations } from "~database/entities/User";
|
type UserWithRelations,
|
||||||
import { userRelations } from "~database/entities/relations";
|
findFirstUser,
|
||||||
|
findManyUsers,
|
||||||
|
userToAPI,
|
||||||
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -32,36 +35,27 @@ export default apiRoute<{
|
||||||
// TODO: Add pinned
|
// TODO: Add pinned
|
||||||
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
|
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (limit < 1 || limit > 40) return errorResponse("Invalid limit", 400);
|
if (limit < 1 || limit > 40) return errorResponse("Invalid limit", 400);
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
||||||
client.user,
|
findManyUsers,
|
||||||
{
|
{
|
||||||
where: {
|
// @ts-ignore
|
||||||
relationshipSubjects: {
|
where: (following, { and, lt, gt, gte, eq, sql }) =>
|
||||||
some: {
|
and(
|
||||||
ownerId: user.id,
|
max_id ? lt(following.id, max_id) : undefined,
|
||||||
following: true,
|
since_id ? gte(following.id, since_id) : undefined,
|
||||||
},
|
min_id ? gt(following.id, min_id) : undefined,
|
||||||
},
|
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${following.id} AND "Relationship"."objectId" = ${otherUser.id} AND "Relationship"."following" = true)`,
|
||||||
id: {
|
),
|
||||||
lt: max_id,
|
// @ts-expect-error Yes I KNOW the types are wrong
|
||||||
gt: min_id,
|
orderBy: (liker, { desc }) => desc(liker.id),
|
||||||
gte: since_id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
take: Number(limit),
|
|
||||||
orderBy: {
|
|
||||||
id: "desc",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./index";
|
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -35,58 +37,31 @@ export default apiRoute<{
|
||||||
|
|
||||||
const { notifications, duration } = extraData.parsedRequest;
|
const { notifications, duration } = extraData.parsedRequest;
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const user = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!user) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, user);
|
||||||
|
|
||||||
if (!relationship) {
|
if (!foundRelationship.muting) {
|
||||||
// Create new relationship
|
foundRelationship.muting = true;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
|
||||||
|
|
||||||
await client.user.update({
|
|
||||||
where: { id: self.id },
|
|
||||||
data: {
|
|
||||||
relationships: {
|
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relationship.muting) {
|
|
||||||
relationship.muting = true;
|
|
||||||
}
|
}
|
||||||
if (notifications ?? true) {
|
if (notifications ?? true) {
|
||||||
relationship.mutingNotifications = true;
|
foundRelationship.mutingNotifications = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.relationship.update({
|
await db
|
||||||
where: { id: relationship.id },
|
.update(relationship)
|
||||||
data: {
|
.set({
|
||||||
muting: true,
|
muting: true,
|
||||||
mutingNotifications: notifications ?? true,
|
mutingNotifications: notifications ?? true,
|
||||||
},
|
})
|
||||||
});
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
|
|
||||||
// TODO: Implement duration
|
// TODO: Implement duration
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -34,50 +36,23 @@ export default apiRoute<{
|
||||||
|
|
||||||
const { comment } = extraData.parsedRequest;
|
const { comment } = extraData.parsedRequest;
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship) {
|
foundRelationship.note = comment ?? "";
|
||||||
// Create new relationship
|
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
|
.set({
|
||||||
|
note: foundRelationship.note,
|
||||||
|
})
|
||||||
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
|
|
||||||
await client.user.update({
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
where: { id: self.id },
|
|
||||||
data: {
|
|
||||||
relationships: {
|
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
|
||||||
|
|
||||||
relationship.note = comment ?? "";
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
note: relationship.note,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,52 +32,25 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship) {
|
if (!foundRelationship.endorsed) {
|
||||||
// Create new relationship
|
foundRelationship.endorsed = true;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
await client.user.update({
|
.set({
|
||||||
where: { id: self.id },
|
endorsed: true,
|
||||||
data: {
|
})
|
||||||
relationships: {
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!relationship.endorsed) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.endorsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
endorsed: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,66 +32,40 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship) {
|
if (foundRelationship.followedBy) {
|
||||||
// Create new relationship
|
foundRelationship.followedBy = false;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
|
.set({
|
||||||
|
followedBy: false,
|
||||||
|
})
|
||||||
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
|
|
||||||
await client.user.update({
|
if (otherUser.instanceId === null) {
|
||||||
where: { id: self.id },
|
// Also remove from followers list
|
||||||
data: {
|
await db
|
||||||
relationships: {
|
.update(relationship)
|
||||||
connect: {
|
.set({
|
||||||
id: newRelationship.id,
|
following: false,
|
||||||
},
|
})
|
||||||
},
|
.where(
|
||||||
},
|
and(
|
||||||
});
|
eq(relationship.ownerId, otherUser.id),
|
||||||
|
eq(relationship.subjectId, self.id),
|
||||||
relationship = newRelationship;
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship.followedBy) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.followedBy = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
followedBy: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user.instanceId === null) {
|
|
||||||
// Also remove from followers list
|
|
||||||
await client.relationship.updateMany({
|
|
||||||
where: {
|
|
||||||
ownerId: user.id,
|
|
||||||
subjectId: self.id,
|
|
||||||
following: true,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
following: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,12 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { sql } from "drizzle-orm";
|
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import {
|
import {
|
||||||
|
type StatusWithRelations,
|
||||||
findManyStatuses,
|
findManyStatuses,
|
||||||
statusToAPI,
|
statusToAPI,
|
||||||
type StatusWithRelations,
|
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import { findFirstUser } from "~database/entities/User";
|
import { findFirstUser } from "~database/entities/User";
|
||||||
import {
|
|
||||||
statusAndUserRelations,
|
|
||||||
userRelations,
|
|
||||||
} from "~database/entities/relations";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -20,9 +22,6 @@ export const meta = applyConfig({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks a user
|
|
||||||
*/
|
|
||||||
export default apiRoute(async (req, matchedRoute, extraData) => {
|
export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
|
|
@ -30,52 +29,24 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
|
||||||
|
|
||||||
if (!relationship) {
|
if (foundRelationship.blocking) {
|
||||||
// Create new relationship
|
foundRelationship.blocking = false;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
await client.user.update({
|
.set({
|
||||||
where: { id: self.id },
|
blocking: false,
|
||||||
data: {
|
})
|
||||||
relationships: {
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship.blocking) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.blocking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
blocking: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,53 +32,26 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship) {
|
if (foundRelationship.following) {
|
||||||
// Create new relationship
|
foundRelationship.following = false;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
await client.user.update({
|
.set({
|
||||||
where: { id: self.id },
|
following: false,
|
||||||
data: {
|
requested: false,
|
||||||
relationships: {
|
})
|
||||||
connect: {
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship.following) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.following = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
following: false,
|
|
||||||
requested: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,54 +32,27 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const user = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!user) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, user);
|
||||||
|
|
||||||
if (!relationship) {
|
if (foundRelationship.muting) {
|
||||||
// Create new relationship
|
foundRelationship.muting = false;
|
||||||
|
foundRelationship.mutingNotifications = false;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
await client.user.update({
|
.set({
|
||||||
where: { id: self.id },
|
muting: false,
|
||||||
data: {
|
mutingNotifications: false,
|
||||||
relationships: {
|
})
|
||||||
connect: {
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship.muting) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.muting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement duration
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
muting: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { relationshipToAPI } from "~database/entities/Relationship";
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
findFirstUser,
|
||||||
relationshipToAPI,
|
getRelationshipToOtherUser,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/User";
|
||||||
import { getRelationshipToOtherUser } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -30,52 +32,25 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const user = await client.user.findUnique({
|
const otherUser = await findFirstUser({
|
||||||
where: { id },
|
where: (user, { eq }) => eq(user.id, id),
|
||||||
include: {
|
|
||||||
relationships: {
|
|
||||||
include: {
|
|
||||||
owner: true,
|
|
||||||
subject: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) return errorResponse("User not found", 404);
|
if (!otherUser) return errorResponse("User not found", 404);
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
let relationship = await getRelationshipToOtherUser(self, user);
|
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
|
||||||
|
|
||||||
if (!relationship) {
|
if (foundRelationship.endorsed) {
|
||||||
// Create new relationship
|
foundRelationship.endorsed = false;
|
||||||
|
|
||||||
const newRelationship = await createNewRelationship(self, user);
|
await db
|
||||||
|
.update(relationship)
|
||||||
await client.user.update({
|
.set({
|
||||||
where: { id: self.id },
|
endorsed: false,
|
||||||
data: {
|
})
|
||||||
relationships: {
|
.where(eq(relationship.id, foundRelationship.id));
|
||||||
connect: {
|
|
||||||
id: newRelationship.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
relationship = newRelationship;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship.endorsed) {
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
relationship.endorsed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.relationship.update({
|
|
||||||
where: { id: relationship.id },
|
|
||||||
data: {
|
|
||||||
endorsed: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { findManyUsers, userToAPI } from "~database/entities/User";
|
||||||
import { userToAPI } from "~database/entities/User";
|
import { db } from "~drizzle/db";
|
||||||
import { userRelations } from "~database/entities/relations";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -34,34 +33,52 @@ export default apiRoute<{
|
||||||
return errorResponse("Number of ids must be between 1 and 10", 422);
|
return errorResponse("Number of ids must be between 1 and 10", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
const followersOfIds = await client.user.findMany({
|
const idFollowerRelationships = await db.query.relationship.findMany({
|
||||||
where: {
|
columns: {
|
||||||
relationships: {
|
ownerId: true,
|
||||||
some: {
|
|
||||||
subjectId: {
|
|
||||||
in: ids,
|
|
||||||
},
|
|
||||||
following: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
where: (relationship, { inArray, and, eq }) =>
|
||||||
|
and(
|
||||||
|
inArray(relationship.subjectId, ids),
|
||||||
|
eq(relationship.following, true),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find users that you follow in followersOfIds
|
if (idFollowerRelationships.length === 0) {
|
||||||
const output = await client.user.findMany({
|
return jsonResponse([]);
|
||||||
where: {
|
}
|
||||||
relationships: {
|
|
||||||
some: {
|
// Find users that you follow in idFollowerRelationships
|
||||||
ownerId: self.id,
|
const relevantRelationships = await db.query.relationship.findMany({
|
||||||
subjectId: {
|
columns: {
|
||||||
in: followersOfIds.map((f) => f.id),
|
subjectId: true,
|
||||||
},
|
|
||||||
following: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
include: userRelations,
|
where: (relationship, { inArray, and, eq }) =>
|
||||||
|
and(
|
||||||
|
eq(relationship.ownerId, self.id),
|
||||||
|
inArray(
|
||||||
|
relationship.subjectId,
|
||||||
|
idFollowerRelationships.map((f) => f.ownerId),
|
||||||
|
),
|
||||||
|
eq(relationship.following, true),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
return jsonResponse(output.map((o) => userToAPI(o)));
|
if (relevantRelationships.length === 0) {
|
||||||
|
return jsonResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalUsers = await findManyUsers({
|
||||||
|
where: (user, { inArray }) =>
|
||||||
|
inArray(
|
||||||
|
user.id,
|
||||||
|
relevantRelationships.map((r) => r.subjectId),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (finalUsers.length === 0) {
|
||||||
|
return jsonResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse(finalUsers.map((o) => userToAPI(o)));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { jsonResponse, response } from "@response";
|
import { jsonResponse, response } from "@response";
|
||||||
import { tempmailDomains } from "@tempmail";
|
import { tempmailDomains } from "@tempmail";
|
||||||
import ISO6391 from "iso-639-1";
|
import ISO6391 from "iso-639-1";
|
||||||
import { client } from "~database/datasource";
|
import { createNewLocalUser, findFirstUser } from "~database/entities/User";
|
||||||
import { createNewLocalUser } from "~database/entities/User";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -127,11 +126,16 @@ export default apiRoute<{
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if username is taken
|
// Check if username is taken
|
||||||
if (await client.user.findFirst({ where: { username: body.username } }))
|
if (
|
||||||
|
await findFirstUser({
|
||||||
|
where: (user, { eq }) => eq(user.username, body.username ?? ""),
|
||||||
|
})
|
||||||
|
) {
|
||||||
errors.details.username.push({
|
errors.details.username.push({
|
||||||
error: "ERR_TAKEN",
|
error: "ERR_TAKEN",
|
||||||
description: "is already taken",
|
description: "is already taken",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check if email is valid
|
// Check if email is valid
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { getUserUri } from "~database/entities/User";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./index";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
import { getUserUri } from "~database/entities/User";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import type { User } from "@prisma/client";
|
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import {
|
import {
|
||||||
createNewRelationship,
|
createNewRelationship,
|
||||||
relationshipToAPI,
|
relationshipToAPI,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/Relationship";
|
||||||
|
import type { User } from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -37,13 +37,12 @@ export default apiRoute<{
|
||||||
return errorResponse("Number of ids must be between 1 and 10", 422);
|
return errorResponse("Number of ids must be between 1 and 10", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
const relationships = await client.relationship.findMany({
|
const relationships = await db.query.relationship.findMany({
|
||||||
where: {
|
where: (relationship, { inArray, and, eq }) =>
|
||||||
ownerId: self.id,
|
and(
|
||||||
subjectId: {
|
inArray(relationship.subjectId, ids),
|
||||||
in: ids,
|
eq(relationship.ownerId, self.id),
|
||||||
},
|
),
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find IDs that dont have a relationship
|
// Find IDs that dont have a relationship
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { getUserUri } from "~database/entities/User";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./index";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
import { getUserUri } from "~database/entities/User";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
type UserWithRelations,
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
resolveWebFinger,
|
resolveWebFinger,
|
||||||
userToAPI,
|
userToAPI,
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
import { user } from "~drizzle/schema";
|
import { user } from "~drizzle/schema";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { convertTextToHtml } from "@formatting";
|
import { convertTextToHtml } from "@formatting";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { sanitizeHtml } from "@sanitization";
|
import { sanitizeHtml } from "@sanitization";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
import ISO6391 from "iso-639-1";
|
import ISO6391 from "iso-639-1";
|
||||||
import { MediaBackendType } from "media-manager";
|
import { MediaBackendType } from "media-manager";
|
||||||
import type { MediaBackend } from "media-manager";
|
import type { MediaBackend } from "media-manager";
|
||||||
import { client } from "~database/datasource";
|
import { LocalMediaBackend, S3MediaBackend } from "media-manager";
|
||||||
import { getUrl } from "~database/entities/Attachment";
|
import { getUrl } from "~database/entities/Attachment";
|
||||||
import { parseEmojis } from "~database/entities/Emoji";
|
import { parseEmojis } from "~database/entities/Emoji";
|
||||||
import { userToAPI } from "~database/entities/User";
|
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||||
import { userRelations } from "~database/entities/relations";
|
import { db } from "~drizzle/db";
|
||||||
import { S3MediaBackend, LocalMediaBackend } from "media-manager";
|
import { emojiToUser, user } from "~drizzle/schema";
|
||||||
import type { APISource } from "~types/entities/source";
|
import type { APISource } from "~types/entities/source";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
@ -38,9 +39,9 @@ export default apiRoute<{
|
||||||
"source[sensitive]": string;
|
"source[sensitive]": string;
|
||||||
"source[language]": string;
|
"source[language]": string;
|
||||||
}>(async (req, matchedRoute, extraData) => {
|
}>(async (req, matchedRoute, extraData) => {
|
||||||
const { user } = extraData.auth;
|
const { user: self } = extraData.auth;
|
||||||
|
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const config = await extraData.configManager.getConfig();
|
const config = await extraData.configManager.getConfig();
|
||||||
|
|
||||||
|
|
@ -109,12 +110,12 @@ export default apiRoute<{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove emojis
|
// Remove emojis
|
||||||
user.emojis = [];
|
self.emojis = [];
|
||||||
|
|
||||||
user.displayName = sanitizedDisplayName;
|
self.displayName = sanitizedDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note && user.source) {
|
if (note && self.source) {
|
||||||
// Check if within allowed note length
|
// Check if within allowed note length
|
||||||
if (sanitizedNote.length > config.validation.max_note_size) {
|
if (sanitizedNote.length > config.validation.max_note_size) {
|
||||||
return errorResponse(
|
return errorResponse(
|
||||||
|
|
@ -128,12 +129,12 @@ export default apiRoute<{
|
||||||
return errorResponse("Bio contains blocked words", 422);
|
return errorResponse("Bio contains blocked words", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
(user.source as APISource).note = sanitizedNote;
|
(self.source as APISource).note = sanitizedNote;
|
||||||
// TODO: Convert note to HTML
|
// TODO: Convert note to HTML
|
||||||
user.note = await convertTextToHtml(sanitizedNote);
|
self.note = await convertTextToHtml(sanitizedNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_privacy && user.source) {
|
if (source_privacy && self.source) {
|
||||||
// Check if within allowed privacy values
|
// Check if within allowed privacy values
|
||||||
if (
|
if (
|
||||||
!["public", "unlisted", "private", "direct"].includes(
|
!["public", "unlisted", "private", "direct"].includes(
|
||||||
|
|
@ -146,19 +147,19 @@ export default apiRoute<{
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(user.source as APISource).privacy = source_privacy;
|
(self.source as APISource).privacy = source_privacy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_sensitive && user.source) {
|
if (source_sensitive && self.source) {
|
||||||
// Check if within allowed sensitive values
|
// Check if within allowed sensitive values
|
||||||
if (source_sensitive !== "true" && source_sensitive !== "false") {
|
if (source_sensitive !== "true" && source_sensitive !== "false") {
|
||||||
return errorResponse("Sensitive must be a boolean", 422);
|
return errorResponse("Sensitive must be a boolean", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
(user.source as APISource).sensitive = source_sensitive === "true";
|
(self.source as APISource).sensitive = source_sensitive === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_language && user.source) {
|
if (source_language && self.source) {
|
||||||
if (!ISO6391.validate(source_language)) {
|
if (!ISO6391.validate(source_language)) {
|
||||||
return errorResponse(
|
return errorResponse(
|
||||||
"Language must be a valid ISO 639-1 code",
|
"Language must be a valid ISO 639-1 code",
|
||||||
|
|
@ -166,7 +167,7 @@ export default apiRoute<{
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(user.source as APISource).language = source_language;
|
(self.source as APISource).language = source_language;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
|
|
@ -180,7 +181,7 @@ export default apiRoute<{
|
||||||
|
|
||||||
const { path } = await mediaManager.addFile(avatar);
|
const { path } = await mediaManager.addFile(avatar);
|
||||||
|
|
||||||
user.avatar = getUrl(path, config);
|
self.avatar = getUrl(path, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header) {
|
if (header) {
|
||||||
|
|
@ -194,7 +195,7 @@ export default apiRoute<{
|
||||||
|
|
||||||
const { path } = await mediaManager.addFile(header);
|
const { path } = await mediaManager.addFile(header);
|
||||||
|
|
||||||
user.header = getUrl(path, config);
|
self.header = getUrl(path, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
|
|
@ -203,7 +204,7 @@ export default apiRoute<{
|
||||||
return errorResponse("Locked must be a boolean", 422);
|
return errorResponse("Locked must be a boolean", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.isLocked = locked === "true";
|
self.isLocked = locked === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bot) {
|
if (bot) {
|
||||||
|
|
@ -212,7 +213,7 @@ export default apiRoute<{
|
||||||
return errorResponse("Bot must be a boolean", 422);
|
return errorResponse("Bot must be a boolean", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.isBot = bot === "true";
|
self.isBot = bot === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discoverable) {
|
if (discoverable) {
|
||||||
|
|
@ -221,7 +222,7 @@ export default apiRoute<{
|
||||||
return errorResponse("Discoverable must be a boolean", 422);
|
return errorResponse("Discoverable must be a boolean", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.isDiscoverable = discoverable === "true";
|
self.isDiscoverable = discoverable === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse emojis
|
// Parse emojis
|
||||||
|
|
@ -229,36 +230,49 @@ export default apiRoute<{
|
||||||
const displaynameEmojis = await parseEmojis(sanitizedDisplayName);
|
const displaynameEmojis = await parseEmojis(sanitizedDisplayName);
|
||||||
const noteEmojis = await parseEmojis(sanitizedNote);
|
const noteEmojis = await parseEmojis(sanitizedNote);
|
||||||
|
|
||||||
user.emojis = [...displaynameEmojis, ...noteEmojis];
|
self.emojis = [...displaynameEmojis, ...noteEmojis];
|
||||||
|
|
||||||
// Deduplicate emojis
|
// Deduplicate emojis
|
||||||
user.emojis = user.emojis.filter(
|
self.emojis = self.emojis.filter(
|
||||||
(emoji, index, self) =>
|
(emoji, index, self) =>
|
||||||
self.findIndex((e) => e.id === emoji.id) === index,
|
self.findIndex((e) => e.id === emoji.id) === index,
|
||||||
);
|
);
|
||||||
|
|
||||||
const output = await client.user.update({
|
await db
|
||||||
where: { id: user.id },
|
.update(user)
|
||||||
data: {
|
.set({
|
||||||
displayName: user.displayName,
|
displayName: self.displayName,
|
||||||
note: user.note,
|
note: self.note,
|
||||||
avatar: user.avatar,
|
avatar: self.avatar,
|
||||||
header: user.header,
|
header: self.header,
|
||||||
isLocked: user.isLocked,
|
isLocked: self.isLocked,
|
||||||
isBot: user.isBot,
|
isBot: self.isBot,
|
||||||
isDiscoverable: user.isDiscoverable,
|
isDiscoverable: self.isDiscoverable,
|
||||||
emojis: {
|
source: self.source || undefined,
|
||||||
disconnect: user.emojis.map((e) => ({
|
})
|
||||||
id: e.id,
|
.where(eq(user.id, self.id));
|
||||||
})),
|
|
||||||
connect: user.emojis.map((e) => ({
|
// Connect emojis, if any
|
||||||
id: e.id,
|
for (const emoji of self.emojis) {
|
||||||
})),
|
await db
|
||||||
},
|
.delete(emojiToUser)
|
||||||
source: user.source || undefined,
|
.where(and(eq(emojiToUser.a, emoji.id), eq(emojiToUser.b, self.id)))
|
||||||
},
|
.execute();
|
||||||
include: userRelations,
|
|
||||||
|
await db
|
||||||
|
.insert(emojiToUser)
|
||||||
|
.values({
|
||||||
|
a: emoji.id,
|
||||||
|
b: self.id,
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = await findFirstUser({
|
||||||
|
where: (user, { eq }) => eq(user.id, self.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!output) return errorResponse("Couldn't edit user", 500);
|
||||||
|
|
||||||
return jsonResponse(userToAPI(output));
|
return jsonResponse(userToAPI(output));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { randomBytes } from "node:crypto";
|
import { randomBytes } from "node:crypto";
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { db } from "~drizzle/db";
|
||||||
|
import { application } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -33,24 +34,28 @@ export default apiRoute<{
|
||||||
return errorResponse("Redirect URI must be a valid URI", 422);
|
return errorResponse("Redirect URI must be a valid URI", 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const application = await client.application.create({
|
|
||||||
data: {
|
const app = (
|
||||||
name: client_name || "",
|
await db
|
||||||
redirect_uris: redirect_uris || "",
|
.insert(application)
|
||||||
scopes: scopes || "read",
|
.values({
|
||||||
website: website || null,
|
name: client_name || "",
|
||||||
client_id: randomBytes(32).toString("base64url"),
|
redirectUris: redirect_uris || "",
|
||||||
secret: randomBytes(64).toString("base64url"),
|
scopes: scopes || "read",
|
||||||
},
|
website: website || null,
|
||||||
});
|
clientId: randomBytes(32).toString("base64url"),
|
||||||
|
secret: randomBytes(64).toString("base64url"),
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
)[0];
|
||||||
|
|
||||||
return jsonResponse({
|
return jsonResponse({
|
||||||
id: application.id,
|
id: app.id,
|
||||||
name: application.name,
|
name: app.name,
|
||||||
website: application.website,
|
website: app.website,
|
||||||
client_id: application.client_id,
|
client_id: app.clientId,
|
||||||
client_secret: application.secret,
|
client_secret: app.secret,
|
||||||
redirect_uri: application.redirect_uris,
|
redirect_uri: app.redirectUris,
|
||||||
vapid_link: application.vapid_key,
|
vapid_link: app.vapidKey,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import {
|
import {
|
||||||
|
type UserWithRelations,
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
userToAPI,
|
userToAPI,
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { emojiToAPI } from "~database/entities/Emoji";
|
import { emojiToAPI } from "~database/entities/Emoji";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import {
|
import {
|
||||||
statusToAPI,
|
|
||||||
type StatusWithRelations,
|
type StatusWithRelations,
|
||||||
|
findManyStatuses,
|
||||||
|
statusToAPI,
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import { statusAndUserRelations } from "~database/entities/relations";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
@ -37,25 +36,18 @@ export default apiRoute<{
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
const { objects, link } = await fetchTimeline<StatusWithRelations>(
|
const { objects, link } = await fetchTimeline<StatusWithRelations>(
|
||||||
client.status,
|
findManyStatuses,
|
||||||
{
|
{
|
||||||
where: {
|
// @ts-ignore
|
||||||
id: {
|
where: (status, { and, lt, gt, gte, eq, sql }) =>
|
||||||
lt: max_id ?? undefined,
|
and(
|
||||||
gte: since_id ?? undefined,
|
max_id ? lt(status.id, max_id) : undefined,
|
||||||
gt: min_id ?? undefined,
|
since_id ? gte(status.id, since_id) : undefined,
|
||||||
},
|
min_id ? gt(status.id, min_id) : undefined,
|
||||||
likes: {
|
sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${status.id} AND "Like"."likerId" = ${user.id})`,
|
||||||
some: {
|
),
|
||||||
likerId: user.id,
|
// @ts-expect-error Yes I KNOW the types are wrong
|
||||||
},
|
orderBy: (status, { desc }) => desc(status.id),
|
||||||
},
|
|
||||||
},
|
|
||||||
include: statusAndUserRelations,
|
|
||||||
take: Number(limit),
|
|
||||||
orderBy: {
|
|
||||||
id: "desc",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
checkForBidirectionalRelationships,
|
checkForBidirectionalRelationships,
|
||||||
relationshipToAPI,
|
relationshipToAPI,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/Relationship";
|
||||||
import { sendFollowAccept } from "~database/entities/User";
|
import {
|
||||||
import { userRelations } from "~database/entities/relations";
|
findFirstUser,
|
||||||
|
getRelationshipToOtherUser,
|
||||||
|
sendFollowAccept,
|
||||||
|
} from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -27,11 +32,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
const { account_id } = matchedRoute.params;
|
const { account_id } = matchedRoute.params;
|
||||||
|
|
||||||
const account = await client.user.findUnique({
|
const account = await findFirstUser({
|
||||||
where: {
|
where: (user, { eq }) => eq(user.id, account_id),
|
||||||
id: account_id,
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!account) return errorResponse("Account not found", 404);
|
if (!account) return errorResponse("Account not found", 404);
|
||||||
|
|
@ -40,37 +42,35 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
await checkForBidirectionalRelationships(user, account);
|
await checkForBidirectionalRelationships(user, account);
|
||||||
|
|
||||||
// Authorize follow request
|
// Authorize follow request
|
||||||
await client.relationship.updateMany({
|
await db
|
||||||
where: {
|
.update(relationship)
|
||||||
subjectId: user.id,
|
.set({
|
||||||
ownerId: account.id,
|
|
||||||
requested: true,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
requested: false,
|
requested: false,
|
||||||
following: true,
|
following: true,
|
||||||
},
|
})
|
||||||
});
|
.where(
|
||||||
|
and(
|
||||||
|
eq(relationship.subjectId, user.id),
|
||||||
|
eq(relationship.ownerId, account.id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Update followedBy for other user
|
// Update followedBy for other user
|
||||||
await client.relationship.updateMany({
|
await db
|
||||||
where: {
|
.update(relationship)
|
||||||
subjectId: account.id,
|
.set({
|
||||||
ownerId: user.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
followedBy: true,
|
followedBy: true,
|
||||||
},
|
})
|
||||||
});
|
.where(
|
||||||
|
and(
|
||||||
|
eq(relationship.subjectId, account.id),
|
||||||
|
eq(relationship.ownerId, user.id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const relationship = await client.relationship.findFirst({
|
const foundRelationship = await getRelationshipToOtherUser(user, account);
|
||||||
where: {
|
|
||||||
subjectId: account.id,
|
|
||||||
ownerId: user.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!relationship) return errorResponse("Relationship not found", 404);
|
if (!foundRelationship) return errorResponse("Relationship not found", 404);
|
||||||
|
|
||||||
// Check if accepting remote follow
|
// Check if accepting remote follow
|
||||||
if (account.instanceId) {
|
if (account.instanceId) {
|
||||||
|
|
@ -78,5 +78,5 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
await sendFollowAccept(account, user);
|
await sendFollowAccept(account, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
checkForBidirectionalRelationships,
|
checkForBidirectionalRelationships,
|
||||||
relationshipToAPI,
|
relationshipToAPI,
|
||||||
} from "~database/entities/Relationship";
|
} from "~database/entities/Relationship";
|
||||||
import { sendFollowReject } from "~database/entities/User";
|
import {
|
||||||
import { userRelations } from "~database/entities/relations";
|
findFirstUser,
|
||||||
|
getRelationshipToOtherUser,
|
||||||
|
sendFollowReject,
|
||||||
|
} from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { relationship } from "~drizzle/schema";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -27,11 +32,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
|
||||||
const { account_id } = matchedRoute.params;
|
const { account_id } = matchedRoute.params;
|
||||||
|
|
||||||
const account = await client.user.findUnique({
|
const account = await findFirstUser({
|
||||||
where: {
|
where: (user, { eq }) => eq(user.id, account_id),
|
||||||
id: account_id,
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!account) return errorResponse("Account not found", 404);
|
if (!account) return errorResponse("Account not found", 404);
|
||||||
|
|
@ -40,25 +42,35 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
await checkForBidirectionalRelationships(user, account);
|
await checkForBidirectionalRelationships(user, account);
|
||||||
|
|
||||||
// Reject follow request
|
// Reject follow request
|
||||||
await client.relationship.updateMany({
|
await db
|
||||||
where: {
|
.update(relationship)
|
||||||
subjectId: user.id,
|
.set({
|
||||||
ownerId: account.id,
|
|
||||||
requested: true,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
requested: false,
|
requested: false,
|
||||||
},
|
following: false,
|
||||||
});
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(relationship.subjectId, user.id),
|
||||||
|
eq(relationship.ownerId, account.id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const relationship = await client.relationship.findFirst({
|
// Update followedBy for other user
|
||||||
where: {
|
await db
|
||||||
subjectId: account.id,
|
.update(relationship)
|
||||||
ownerId: user.id,
|
.set({
|
||||||
},
|
followedBy: false,
|
||||||
});
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(relationship.subjectId, account.id),
|
||||||
|
eq(relationship.ownerId, user.id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (!relationship) return errorResponse("Relationship not found", 404);
|
const foundRelationship = await getRelationshipToOtherUser(user, account);
|
||||||
|
|
||||||
|
if (!foundRelationship) return errorResponse("Relationship not found", 404);
|
||||||
|
|
||||||
// Check if rejecting remote follow
|
// Check if rejecting remote follow
|
||||||
if (account.instanceId) {
|
if (account.instanceId) {
|
||||||
|
|
@ -66,5 +78,5 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
await sendFollowReject(account, user);
|
await sendFollowReject(account, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonResponse(relationshipToAPI(relationship));
|
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import {
|
import {
|
||||||
|
type UserWithRelations,
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
userToAPI,
|
userToAPI,
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import { count, isNull } from "drizzle-orm";
|
import { and, count, countDistinct, eq, gte, isNull } from "drizzle-orm";
|
||||||
import { client } from "~database/datasource";
|
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||||
import { userToAPI } from "~database/entities/User";
|
|
||||||
import { userRelations } from "~database/entities/relations";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { status, user } from "~drizzle/schema";
|
import { instance, status, user } from "~drizzle/schema";
|
||||||
import manifest from "~package.json";
|
import manifest from "~package.json";
|
||||||
import type { APIInstance } from "~types/entities/instance";
|
import type { APIInstance } from "~types/entities/instance";
|
||||||
|
|
||||||
|
|
@ -45,33 +43,39 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
.where(isNull(user.instanceId))
|
.where(isNull(user.instanceId))
|
||||||
)[0].count;
|
)[0].count;
|
||||||
|
|
||||||
// Get the first created admin user
|
const contactAccount = await findFirstUser({
|
||||||
const contactAccount = await client.user.findFirst({
|
where: (user, { isNull, eq, and }) =>
|
||||||
where: {
|
and(isNull(user.instanceId), eq(user.isAdmin, true)),
|
||||||
instanceId: null,
|
orderBy: (user, { asc }) => asc(user.id),
|
||||||
isAdmin: true,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
id: "asc",
|
|
||||||
},
|
|
||||||
include: userRelations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get user that have posted once in the last 30 days
|
const monthlyActiveUsers = (
|
||||||
const monthlyActiveUsers = await client.user.count({
|
await db
|
||||||
where: {
|
.select({
|
||||||
instanceId: null,
|
count: countDistinct(user),
|
||||||
statuses: {
|
})
|
||||||
some: {
|
.from(user)
|
||||||
createdAt: {
|
.leftJoin(status, eq(user.id, status.authorId))
|
||||||
gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
|
.where(
|
||||||
},
|
and(
|
||||||
},
|
isNull(user.instanceId),
|
||||||
},
|
gte(
|
||||||
},
|
status.createdAt,
|
||||||
});
|
new Date(
|
||||||
|
Date.now() - 30 * 24 * 60 * 60 * 1000,
|
||||||
|
).toISOString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)[0].count;
|
||||||
|
|
||||||
const knownDomainsCount = await client.instance.count();
|
const knownDomainsCount = (
|
||||||
|
await db
|
||||||
|
.select({
|
||||||
|
count: count(),
|
||||||
|
})
|
||||||
|
.from(instance)
|
||||||
|
)[0].count;
|
||||||
|
|
||||||
// TODO: fill in more values
|
// TODO: fill in more values
|
||||||
return jsonResponse({
|
return jsonResponse({
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse, response } from "@response";
|
import { errorResponse, jsonResponse, response } from "@response";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
import type { MediaBackend } from "media-manager";
|
import type { MediaBackend } from "media-manager";
|
||||||
import { MediaBackendType } from "media-manager";
|
import { MediaBackendType } from "media-manager";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { attachment } from "~drizzle/schema";
|
||||||
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
|
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
@ -35,13 +37,11 @@ export default apiRoute<{
|
||||||
|
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
const attachment = await client.attachment.findUnique({
|
const foundAttachment = await db.query.attachment.findFirst({
|
||||||
where: {
|
where: (attachment, { eq }) => eq(attachment.id, id),
|
||||||
id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!attachment) {
|
if (!foundAttachment) {
|
||||||
return errorResponse("Media not found", 404);
|
return errorResponse("Media not found", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,15 +49,15 @@ export default apiRoute<{
|
||||||
|
|
||||||
switch (req.method) {
|
switch (req.method) {
|
||||||
case "GET": {
|
case "GET": {
|
||||||
if (attachment.url) {
|
if (foundAttachment.url) {
|
||||||
return jsonResponse(attachmentToAPI(attachment));
|
return jsonResponse(attachmentToAPI(foundAttachment));
|
||||||
}
|
}
|
||||||
return response(null, 206);
|
return response(null, 206);
|
||||||
}
|
}
|
||||||
case "PUT": {
|
case "PUT": {
|
||||||
const { description, thumbnail } = extraData.parsedRequest;
|
const { description, thumbnail } = extraData.parsedRequest;
|
||||||
|
|
||||||
let thumbnailUrl = attachment.thumbnail_url;
|
let thumbnailUrl = foundAttachment.thumbnailUrl;
|
||||||
|
|
||||||
let mediaManager: MediaBackend;
|
let mediaManager: MediaBackend;
|
||||||
|
|
||||||
|
|
@ -78,26 +78,27 @@ export default apiRoute<{
|
||||||
thumbnailUrl = getUrl(path, config);
|
thumbnailUrl = getUrl(path, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const descriptionText = description || attachment.description;
|
const descriptionText = description || foundAttachment.description;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
descriptionText !== attachment.description ||
|
descriptionText !== foundAttachment.description ||
|
||||||
thumbnailUrl !== attachment.thumbnail_url
|
thumbnailUrl !== foundAttachment.thumbnailUrl
|
||||||
) {
|
) {
|
||||||
const newAttachment = await client.attachment.update({
|
const newAttachment = (
|
||||||
where: {
|
await db
|
||||||
id,
|
.update(attachment)
|
||||||
},
|
.set({
|
||||||
data: {
|
description: descriptionText,
|
||||||
description: descriptionText,
|
thumbnailUrl,
|
||||||
thumbnail_url: thumbnailUrl,
|
})
|
||||||
},
|
.where(eq(attachment.id, id))
|
||||||
});
|
.returning()
|
||||||
|
)[0];
|
||||||
|
|
||||||
return jsonResponse(attachmentToAPI(newAttachment));
|
return jsonResponse(attachmentToAPI(newAttachment));
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonResponse(attachmentToAPI(attachment));
|
return jsonResponse(attachmentToAPI(foundAttachment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ import { encode } from "blurhash";
|
||||||
import { MediaBackendType } from "media-manager";
|
import { MediaBackendType } from "media-manager";
|
||||||
import type { MediaBackend } from "media-manager";
|
import type { MediaBackend } from "media-manager";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { attachment } from "~drizzle/schema";
|
||||||
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
|
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
@ -132,20 +133,22 @@ export default apiRoute<{
|
||||||
thumbnailUrl = getUrl(path, config);
|
thumbnailUrl = getUrl(path, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newAttachment = await client.attachment.create({
|
const newAttachment = (
|
||||||
data: {
|
await db
|
||||||
url,
|
.insert(attachment)
|
||||||
thumbnail_url: thumbnailUrl,
|
.values({
|
||||||
sha256: sha256.update(await file.arrayBuffer()).digest("hex"),
|
url,
|
||||||
mime_type: file.type,
|
thumbnailUrl,
|
||||||
description: description ?? "",
|
sha256: sha256.update(await file.arrayBuffer()).digest("hex"),
|
||||||
size: file.size,
|
mimeType: file.type,
|
||||||
blurhash: blurhash ?? undefined,
|
description: description ?? "",
|
||||||
width: metadata?.width ?? undefined,
|
size: file.size,
|
||||||
height: metadata?.height ?? undefined,
|
blurhash: blurhash ?? undefined,
|
||||||
},
|
width: metadata?.width ?? undefined,
|
||||||
});
|
height: metadata?.height ?? undefined,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
)[0];
|
||||||
// TODO: Add job to process videos and other media
|
// TODO: Add job to process videos and other media
|
||||||
|
|
||||||
return jsonResponse(attachmentToAPI(newAttachment));
|
return jsonResponse(attachmentToAPI(newAttachment));
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import {
|
import {
|
||||||
|
type UserWithRelations,
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
userToAPI,
|
userToAPI,
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
import { userRelations } from "~database/entities/relations";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,11 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import {
|
import {
|
||||||
findManyNotifications,
|
findManyNotifications,
|
||||||
notificationToAPI,
|
notificationToAPI,
|
||||||
} from "~database/entities/Notification";
|
} from "~database/entities/Notification";
|
||||||
import {
|
import type { NotificationWithRelations } from "~database/entities/Notification";
|
||||||
statusAndUserRelations,
|
|
||||||
userRelations,
|
|
||||||
} from "~database/entities/relations";
|
|
||||||
import type {
|
|
||||||
Notification,
|
|
||||||
NotificationWithRelations,
|
|
||||||
} from "~database/entities/Notification";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { createLike } from "~database/entities/Like";
|
import { createLike } from "~database/entities/Like";
|
||||||
import {
|
import {
|
||||||
findFirstStatuses,
|
findFirstStatuses,
|
||||||
isViewableByUser,
|
isViewableByUser,
|
||||||
statusToAPI,
|
statusToAPI,
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import { statusAndUserRelations } from "~database/entities/relations";
|
import { db } from "~drizzle/db";
|
||||||
import type { APIStatus } from "~types/entities/status";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
@ -40,11 +39,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
if (!status || !isViewableByUser(status, user))
|
if (!status || !isViewableByUser(status, user))
|
||||||
return errorResponse("Record not found", 404);
|
return errorResponse("Record not found", 404);
|
||||||
|
|
||||||
const existingLike = await client.like.findFirst({
|
const existingLike = await db.query.like.findFirst({
|
||||||
where: {
|
where: (like, { and, eq }) =>
|
||||||
likedId: status.id,
|
and(eq(like.likedId, status.id), eq(like.likerId, user.id)),
|
||||||
likerId: user.id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!existingLike) {
|
if (!existingLike) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./favourited_by";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
|
import { meta } from "./favourited_by";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
@ -21,7 +20,7 @@ afterAll(async () => {
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
for (const status of timeline) {
|
for (const status of timeline) {
|
||||||
await fetch(
|
const res = await fetch(
|
||||||
new URL(
|
new URL(
|
||||||
`/api/v1/statuses/${status.id}/favourite`,
|
`/api/v1/statuses/${status.id}/favourite`,
|
||||||
config.http.base_url,
|
config.http.base_url,
|
||||||
|
|
@ -29,6 +28,7 @@ beforeAll(async () => {
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
|
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
|
||||||
import {
|
import {
|
||||||
|
type UserWithRelations,
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
userToAPI,
|
userToAPI,
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./reblogged_by";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./reblogged_by";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
@ -29,6 +29,7 @@ beforeAll(async () => {
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import { fetchTimeline } from "@timelines";
|
||||||
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
|
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
|
||||||
import {
|
import {
|
||||||
type UserWithRelations,
|
type UserWithRelations,
|
||||||
userToAPI,
|
|
||||||
findManyUsers,
|
findManyUsers,
|
||||||
|
userToAPI,
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { afterAll, describe, expect, test } from "bun:test";
|
import { afterAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./index";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { sanitizeHtml } from "@sanitization";
|
import { sanitizeHtml } from "@sanitization";
|
||||||
import { parse } from "marked";
|
import { parse } from "marked";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { getFromToken } from "~database/entities/Application";
|
|
||||||
import type { StatusWithRelations } from "~database/entities/Status";
|
import type { StatusWithRelations } from "~database/entities/Status";
|
||||||
import {
|
import {
|
||||||
createNewStatus,
|
createNewStatus,
|
||||||
|
|
@ -12,10 +10,7 @@ import {
|
||||||
parseTextMentions,
|
parseTextMentions,
|
||||||
statusToAPI,
|
statusToAPI,
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import type { UserWithRelations } from "~database/entities/User";
|
|
||||||
import { statusAndUserRelations } from "~database/entities/relations";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { afterAll, describe, expect, test } from "bun:test";
|
import { afterAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./home";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./home";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import {
|
import {
|
||||||
type StatusWithRelations,
|
type StatusWithRelations,
|
||||||
statusToAPI,
|
|
||||||
findManyStatuses,
|
findManyStatuses,
|
||||||
|
statusToAPI,
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { afterAll, describe, expect, test } from "bun:test";
|
import { afterAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "~index";
|
||||||
import {
|
import {
|
||||||
deleteOldTestUsers,
|
deleteOldTestUsers,
|
||||||
getTestStatuses,
|
getTestStatuses,
|
||||||
getTestUsers,
|
getTestUsers,
|
||||||
sendTestRequest,
|
sendTestRequest,
|
||||||
} from "~tests/utils";
|
} from "~tests/utils";
|
||||||
import { config } from "~index";
|
|
||||||
import { meta } from "./public";
|
|
||||||
import type { APIStatus } from "~types/entities/status";
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
import { meta } from "./public";
|
||||||
|
|
||||||
await deleteOldTestUsers();
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { fetchTimeline } from "@timelines";
|
import { fetchTimeline } from "@timelines";
|
||||||
import { client } from "~database/datasource";
|
import { client } from "~database/datasource";
|
||||||
import {
|
import {
|
||||||
|
type StatusWithRelations,
|
||||||
findManyStatuses,
|
findManyStatuses,
|
||||||
statusToAPI,
|
statusToAPI,
|
||||||
type StatusWithRelations,
|
|
||||||
} from "~database/entities/Status";
|
} from "~database/entities/Status";
|
||||||
import { statusAndUserRelations } from "~database/entities/relations";
|
import { statusAndUserRelations } from "~database/entities/relations";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
|
import type * as Lysand from "lysand-types";
|
||||||
import { client } from "~database/datasource";
|
import { client } from "~database/datasource";
|
||||||
|
import { statusToLysand } from "~database/entities/Status";
|
||||||
import { userToLysand } from "~database/entities/User";
|
import { userToLysand } from "~database/entities/User";
|
||||||
import { statusAndUserRelations } from "~database/entities/relations";
|
import { statusAndUserRelations } from "~database/entities/relations";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { statusToLysand } from "~database/entities/Status";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["GET"],
|
allowedMethods: ["GET"],
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import { apiRoute, applyConfig } from "@api";
|
import { apiRoute, applyConfig } from "@api";
|
||||||
import { errorResponse, response } from "@response";
|
import { errorResponse, response } from "@response";
|
||||||
import { client } from "~database/datasource";
|
|
||||||
import { userRelations } from "~database/entities/relations";
|
|
||||||
import type * as Lysand from "lysand-types";
|
import type * as Lysand from "lysand-types";
|
||||||
|
import { client } from "~database/datasource";
|
||||||
|
import { objectToInboxRequest } from "~database/entities/Federation";
|
||||||
import { createNewStatus, resolveStatus } from "~database/entities/Status";
|
import { createNewStatus, resolveStatus } from "~database/entities/Status";
|
||||||
import type { APIStatus } from "~types/entities/status";
|
|
||||||
import {
|
import {
|
||||||
followAcceptToLysand,
|
followAcceptToLysand,
|
||||||
getRelationshipToOtherUser,
|
getRelationshipToOtherUser,
|
||||||
resolveUser,
|
resolveUser,
|
||||||
sendFollowAccept,
|
sendFollowAccept,
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
import { objectToInboxRequest } from "~database/entities/Federation";
|
import { userRelations } from "~database/entities/relations";
|
||||||
|
import type { APIStatus } from "~types/entities/status";
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
import type { Token } from "@prisma/client";
|
import type { Token } from "@prisma/client";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import { inArray } from "drizzle-orm";
|
||||||
import { client } from "~database/datasource";
|
import { client } from "~database/datasource";
|
||||||
import { TokenType } from "~database/entities/Token";
|
import { TokenType } from "~database/entities/Token";
|
||||||
import {
|
import {
|
||||||
type UserWithRelations,
|
type UserWithRelations,
|
||||||
createNewLocalUser,
|
createNewLocalUser,
|
||||||
} from "~database/entities/User";
|
} from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { application, user } from "~drizzle/schema";
|
||||||
import type { APIEmoji } from "~types/entities/emoji";
|
import type { APIEmoji } from "~types/entities/emoji";
|
||||||
import type { APIInstance } from "~types/entities/instance";
|
import type { APIInstance } from "~types/entities/instance";
|
||||||
import { sendTestRequest, wrapRelativeUrl } from "./utils";
|
import { sendTestRequest, wrapRelativeUrl } from "./utils";
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
import { inArray } from "drizzle-orm";
|
|
||||||
import { application, user } from "~drizzle/schema";
|
|
||||||
|
|
||||||
const base_url = config.http.base_url;
|
const base_url = config.http.base_url;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import {
|
|
||||||
createNewLocalUser,
|
|
||||||
type User,
|
|
||||||
type UserWithRelations,
|
|
||||||
} from "~database/entities/User";
|
|
||||||
import { randomBytes } from "node:crypto";
|
import { randomBytes } from "node:crypto";
|
||||||
import { server } from "~index";
|
|
||||||
import { db } from "~drizzle/db";
|
|
||||||
import { status, token, user } from "~drizzle/schema";
|
|
||||||
import { inArray, like } from "drizzle-orm";
|
import { inArray, like } from "drizzle-orm";
|
||||||
import type { Status } from "~database/entities/Status";
|
import type { Status } from "~database/entities/Status";
|
||||||
|
import {
|
||||||
|
type User,
|
||||||
|
type UserWithRelations,
|
||||||
|
createNewLocalUser,
|
||||||
|
} from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { status, token, user } from "~drizzle/schema";
|
||||||
|
import { server } from "~index";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This allows us to send a test request to the server even when it isnt running
|
* This allows us to send a test request to the server even when it isnt running
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import type { findManyStatuses, Status } from "~database/entities/Status";
|
|
||||||
import type { findManyUsers, User } from "~database/entities/User";
|
|
||||||
import type {
|
|
||||||
findManyNotifications,
|
|
||||||
Notification,
|
|
||||||
} from "~database/entities/Notification";
|
|
||||||
import type { db } from "~drizzle/db";
|
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
|
import type {
|
||||||
|
Notification,
|
||||||
|
findManyNotifications,
|
||||||
|
} from "~database/entities/Notification";
|
||||||
|
import type { Status, findManyStatuses } from "~database/entities/Status";
|
||||||
|
import type { User, findManyUsers } from "~database/entities/User";
|
||||||
|
import type { db } from "~drizzle/db";
|
||||||
|
|
||||||
export async function fetchTimeline<T extends User | Status | Notification>(
|
export async function fetchTimeline<T extends User | Status | Notification>(
|
||||||
model:
|
model:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue