More work on converting old Prisma calls to Drizzle

This commit is contained in:
Jesse Wierzbinski 2024-04-13 02:20:12 -10:00
parent 66922faa51
commit ad0bf1a350
No known key found for this signature in database
82 changed files with 2580 additions and 5631 deletions

View file

@ -2,19 +2,34 @@
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
"organizeImports": {
"enabled": true,
"ignore": ["node_modules/**/*", "dist/**/*"]
"ignore": [
"node_modules/**/*",
"dist/**/*",
"packages/frontend/.output",
"packages/frontend/.nuxt"
]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
},
"ignore": ["node_modules/**/*", "dist/**/*"]
"ignore": [
"node_modules/**/*",
"dist/**/*",
"packages/frontend/.output",
"packages/frontend/.nuxt"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 4,
"ignore": ["node_modules/**/*", "dist/**/*"]
"ignore": [
"node_modules/**/*",
"dist/**/*",
"packages/frontend/.output",
"packages/frontend/.nuxt"
]
}
}

2
cli.ts
View file

@ -9,12 +9,12 @@ import { CliBuilder, CliCommand } from "cli-parser";
import Table from "cli-table";
import extract from "extract-zip";
import { MediaBackend } from "media-manager";
import { lookup } from "mime-types";
import { client } from "~database/datasource";
import { getUrl } from "~database/entities/Attachment";
import { createNewLocalUser } from "~database/entities/User";
import { CliParameterType } from "~packages/cli-parser/cli-builder.type";
import { config } from "~packages/config-manager";
import { lookup } from "mime-types";
const args = process.argv;

View file

@ -1,11 +1,11 @@
import type { Config } from "config-manager";
import { MediaBackendType } from "media-manager";
import type { APIAsyncAttachment } from "~types/entities/async_attachment";
import type { APIAttachment } from "~types/entities/attachment";
import type { InferSelectModel } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { MediaBackendType } from "media-manager";
import { db } from "~drizzle/db";
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>;

View file

@ -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 { addInstanceIfNotExists } from "./Instance";
import { db } from "~drizzle/db";
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> & {
instance: InferSelectModel<typeof instance> | null;

View file

@ -1,6 +1,6 @@
import type * as Lysand from "lysand-types";
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 (
object: Lysand.Entity,

View file

@ -1,5 +1,5 @@
import { db } from "~drizzle/db";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { instance } from "~drizzle/schema";
/**

View file

@ -1,10 +1,10 @@
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 { 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>;

View file

@ -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 StatusWithRelations,
statusToAPI,
findFirstStatuses,
statusToAPI,
} from "./Status";
import {
type UserWithRelations,
userToAPI,
userRelations,
userExtrasTemplate,
transformOutputToUserWithRelations,
userExtrasTemplate,
userRelations,
userToAPI,
} 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>;

View file

@ -1,8 +1,8 @@
import type { InferSelectModel } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { lysandObject } from "~drizzle/schema";
import { findFirstUser } from "./User";
import type * as Lysand from "lysand-types";
export type LysandObject = InferSelectModel<typeof lysandObject>;

View file

@ -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 { 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>;

View file

@ -1,13 +1,34 @@
import { sanitizeHtml } from "@sanitization";
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 linkifyHtml from "linkify-html";
import linkifyStr from "linkify-string";
import type * as Lysand from "lysand-types";
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 { APIStatus } from "~types/entities/status";
import type { Note } from "~types/lysand/Object";
import type * as Lysand from "lysand-types";
import { applicationToAPI } from "./Application";
import {
attachmentFromLysand,
@ -15,48 +36,27 @@ import {
attachmentToLysand,
} from "./Attachment";
import {
type EmojiWithInstance,
emojiToAPI,
emojiToLysand,
fetchEmoji,
parseEmojis,
type EmojiWithInstance,
} from "./Emoji";
import { objectToInboxRequest } from "./Federation";
import {
getUserUri,
resolveUser,
resolveWebFinger,
userToAPI,
userExtras,
userRelations,
userExtrasTemplate,
type User,
type UserWithRelations,
type UserWithRelationsAndRelationships,
transformOutputToUserWithRelations,
findManyUsers,
getUserUri,
resolveUser,
resolveWebFinger,
transformOutputToUserWithRelations,
userExtras,
userExtrasTemplate,
userRelations,
userToAPI,
} 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>;

View file

@ -1,20 +1,10 @@
import { getBestContentType, urlToContentFormat } from "@content_types";
import { addUserToMeilisearch } from "@meilisearch";
import { type Config, config } from "config-manager";
import { type InferSelectModel, and, eq, sql } from "drizzle-orm";
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 {
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 { db } from "~drizzle/db";
import {
emojiToUser,
instance,
@ -22,7 +12,17 @@ import {
relationship,
user,
} 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> & {
endpoints?: Partial<{

View file

@ -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

View file

@ -1,13 +0,0 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712812153499,
"tag": "0000_third_misty_knight",
"breakpoints": true
}
]
}

View file

@ -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(),
});

View file

@ -1,5 +1,5 @@
import type { Config } from "drizzle-kit";
import { config } from "config-manager";
import type { Config } from "drizzle-kit";
export default {
driver: "pg",

View file

@ -1,6 +1,6 @@
import { Client } from "pg";
import { config } from "config-manager";
import { drizzle } from "drizzle-orm/node-postgres";
import { Client } from "pg";
import * as schema from "./schema";
export const client = new Client({

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712805159664,
"tag": "0000_illegal_living_lightning",
"breakpoints": true
}
]
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712805159664,
"tag": "0000_illegal_living_lightning",
"breakpoints": true
}
]
}

View file

@ -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 {
boolean,
foreignKey,
index,
integer,
jsonb,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
export const emoji = pgTable("Emoji", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),

View file

@ -4,11 +4,11 @@ import { connectMeili } from "@meilisearch";
import { moduleIsEntry } from "@module";
import { initializeRedisCache } from "@redis";
import { config } from "config-manager";
import { count, sql } from "drizzle-orm";
import { LogLevel, LogManager, MultiLogManager } from "log-manager";
import { createServer } from "~server";
import { db, client as pgClient } from "~drizzle/db";
import { status } from "~drizzle/schema";
import { count, sql } from "drizzle-orm";
import { createServer } from "~server";
await pgClient.connect();
const timeAtStart = performance.now();

View file

@ -1,139 +1,139 @@
{
"name": "lysand",
"module": "index.ts",
"type": "module",
"version": "0.4.0",
"description": "A project to build a federated social network",
"author": {
"email": "contact@cpluspatch.com",
"name": "CPlusPatch",
"url": "https://cpluspatch.com"
},
"bugs": {
"url": "https://github.com/lysand-org/lysand/issues"
},
"icon": "https://github.com/lysand-org/lysand",
"license": "AGPL-3.0",
"keywords": ["federated", "activitypub", "bun"],
"workspaces": ["packages/*"],
"maintainers": [
{
"email": "contact@cpluspatch.com",
"name": "CPlusPatch",
"url": "https://cpluspatch.com"
"name": "lysand",
"module": "index.ts",
"type": "module",
"version": "0.4.0",
"description": "A project to build a federated social network",
"author": {
"email": "contact@cpluspatch.com",
"name": "CPlusPatch",
"url": "https://cpluspatch.com"
},
"bugs": {
"url": "https://github.com/lysand-org/lysand/issues"
},
"icon": "https://github.com/lysand-org/lysand",
"license": "AGPL-3.0",
"keywords": ["federated", "activitypub", "bun"],
"workspaces": ["packages/*"],
"maintainers": [
{
"email": "contact@cpluspatch.com",
"name": "CPlusPatch",
"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"
}
}

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ETailwind, EButton } from "vue-email";
import { EButton, ETailwind } from "vue-email";
import tailwindConfig from "~/tailwind.config";
defineProps<{

View file

@ -1,22 +1,22 @@
{
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"c12": "^1.10.0",
"nuxt": "^3.11.2",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@vue-email/nuxt": "^0.8.19"
}
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"c12": "^1.10.0",
"nuxt": "^3.11.2",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@vue-email/nuxt": "^0.8.19"
}
}

View file

@ -10,9 +10,9 @@
</template>
<script setup lang="ts">
import { useRoute } from "vue-router";
import { getHighlighterCore } from "shiki/core";
import getWasm from "shiki/wasm";
import { useRoute } from "vue-router";
const config = (await useFetch("/api/config")).data.value;

View file

@ -10,9 +10,9 @@
</template>
<script setup lang="ts">
import { useRoute } from "vue-router";
import { getHighlighterCore } from "shiki/core";
import getWasm from "shiki/wasm";
import { useRoute } from "vue-router";
const config = (await useFetch("/api/config")).data.value;

View file

@ -1,5 +1,5 @@
import type { Config } from "tailwindcss";
import forms from "@tailwindcss/forms";
import type { Config } from "tailwindcss";
// Default are on https://tailwindcss.nuxtjs.org/tailwind/config#default-configuration
export default (<Partial<Config>>{

View file

@ -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 type { BunFile } from "bun";

View file

@ -1,5 +1,5 @@
import type { Config } from "config-manager";
import { S3Client } from "@jsr/bradenmacdonald__s3-lite-client";
import type { Config } from "config-manager";
import type { ConvertableMediaFormats } from "./media-converter";
import { MediaConverter } from "./media-converter";

View file

@ -3,10 +3,10 @@ import type { S3Client } from "@jsr/bradenmacdonald__s3-lite-client";
import type { Config } from "config-manager";
// FILEPATH: /home/jessew/Dev/lysand/packages/media-manager/backends/s3.test.ts
import {
LocalMediaBackend,
MediaBackend,
MediaBackendType,
MediaHasher,
LocalMediaBackend,
S3MediaBackend,
} from "..";
import { ConvertableMediaFormats, MediaConverter } from "../media-converter";

View file

@ -1,8 +1,9 @@
import { randomBytes } from "node:crypto";
import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource";
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({
allowedMethods: ["POST"],
@ -47,45 +48,28 @@ export default apiRoute<{
if (!email || !password)
return redirectToLogin("Invalid username or password");
// Get user
const user = await client.user.findFirst({
where: {
email,
},
include: userRelations,
const user = await findFirstUser({
where: (user, { eq }) => eq(user.email, email),
});
if (!user || !(await Bun.password.verify(password, user.password || "")))
return redirectToLogin("Invalid username or password");
// Get application
const application = await client.application.findFirst({
where: {
client_id,
},
const application = await db.query.application.findFirst({
where: (app, { eq }) => eq(app.clientId, client_id),
});
if (!application) return redirectToLogin("Invalid client_id");
const code = randomBytes(32).toString("hex");
await client.application.update({
where: { id: application.id },
data: {
tokens: {
create: {
access_token: randomBytes(64).toString("base64url"),
code: code,
scope: scopes.join(" "),
token_type: TokenType.BEARER,
user: {
connect: {
id: user.id,
},
},
},
},
},
await db.insert(token).values({
accessToken: randomBytes(64).toString("base64url"),
code: code,
scope: scopes.join(" "),
tokenType: TokenType.BEARER,
applicationId: application.id,
userId: user.id,
});
// Redirect to OAuth confirmation screen

View file

@ -1,6 +1,7 @@
import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource";
import { userRelations } from "~database/entities/relations";
import { and, eq } from "drizzle-orm";
import { db } from "~drizzle/db";
import { application, token } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -34,23 +35,15 @@ export default apiRoute<{
302,
);
// Get token
const token = await client.token.findFirst({
where: {
code,
application: {
client_id,
},
},
include: {
user: {
include: userRelations,
},
application: true,
},
});
const foundToken = await db
.select()
.from(token)
.leftJoin(application, eq(token.applicationId, application.id))
.where(and(eq(token.code, code), eq(application.clientId, client_id)))
.limit(1);
if (!token) return redirectToLogin("Invalid code");
if (!foundToken || foundToken.length <= 0)
return redirectToLogin("Invalid code");
// Redirect back to application
return Response.redirect(`${redirect_uri}?code=${code}`, 302);

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,52 +32,24 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
if (!foundRelationship.blocking) {
foundRelationship.blocking = true;
}
if (!relationship.blocking) {
relationship.blocking = true;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
await db
.update(relationship)
.set({
blocking: true,
},
});
})
.where(eq(relationship.id, foundRelationship.id));
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,8 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
findFirstUser,
followRequestUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
@ -39,27 +36,19 @@ export default apiRoute<{
const { languages, notify, reblogs } = extraData.parsedRequest;
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
let relationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship.following) {
relationship = await followRequestUser(
self,
user,
otherUser,
relationship.id,
reblogs,
notify,

View file

@ -1,9 +1,12 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import { type UserWithRelations, userToAPI } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import {
type UserWithRelations,
findFirstUser,
findManyUsers,
userToAPI,
} from "~database/entities/User";
export const meta = applyConfig({
allowedMethods: ["GET"],
@ -32,36 +35,27 @@ export default apiRoute<{
// TODO: Add pinned
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
const user = await client.user.findUnique({
where: { id },
include: userRelations,
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
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>(
client.user,
findManyUsers,
{
where: {
relationships: {
some: {
subjectId: user.id,
following: true,
},
},
id: {
lt: max_id,
gt: min_id,
gte: since_id,
},
},
include: userRelations,
take: Number(limit),
orderBy: {
id: "desc",
},
// @ts-ignore
where: (follower, { and, lt, gt, gte, eq, sql }) =>
and(
max_id ? lt(follower.id, max_id) : undefined,
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)`,
),
// @ts-expect-error Yes I KNOW the types are wrong
orderBy: (liker, { desc }) => desc(liker.id),
},
req,
);

View file

@ -1,9 +1,12 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import { userToAPI, type UserWithRelations } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import {
type UserWithRelations,
findFirstUser,
findManyUsers,
userToAPI,
} from "~database/entities/User";
export const meta = applyConfig({
allowedMethods: ["GET"],
@ -32,36 +35,27 @@ export default apiRoute<{
// TODO: Add pinned
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
const user = await client.user.findUnique({
where: { id },
include: userRelations,
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
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>(
client.user,
findManyUsers,
{
where: {
relationshipSubjects: {
some: {
ownerId: user.id,
following: true,
},
},
id: {
lt: max_id,
gt: min_id,
gte: since_id,
},
},
include: userRelations,
take: Number(limit),
orderBy: {
id: "desc",
},
// @ts-ignore
where: (following, { and, lt, gt, gte, eq, sql }) =>
and(
max_id ? lt(following.id, max_id) : undefined,
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)`,
),
// @ts-expect-error Yes I KNOW the types are wrong
orderBy: (liker, { desc }) => desc(liker.id),
},
req,
);

View file

@ -1,13 +1,13 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} from "~tests/utils";
import { config } from "~index";
import { meta } from "./index";
import type { APIAccount } from "~types/entities/account";
import { meta } from "./index";
await deleteOldTestUsers();

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -35,58 +37,31 @@ export default apiRoute<{
const { notifications, duration } = extraData.parsedRequest;
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const user = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, user);
if (!relationship) {
// Create new relationship
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 (!foundRelationship.muting) {
foundRelationship.muting = true;
}
if (notifications ?? true) {
relationship.mutingNotifications = true;
foundRelationship.mutingNotifications = true;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
await db
.update(relationship)
.set({
muting: true,
mutingNotifications: notifications ?? true,
},
});
})
.where(eq(relationship.id, foundRelationship.id));
// TODO: Implement duration
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -34,50 +36,23 @@ export default apiRoute<{
const { comment } = extraData.parsedRequest;
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
foundRelationship.note = comment ?? "";
const newRelationship = await createNewRelationship(self, user);
await db
.update(relationship)
.set({
note: foundRelationship.note,
})
.where(eq(relationship.id, foundRelationship.id));
await client.user.update({
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));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,52 +32,25 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
if (!foundRelationship.endorsed) {
foundRelationship.endorsed = true;
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
await db
.update(relationship)
.set({
endorsed: true,
})
.where(eq(relationship.id, foundRelationship.id));
}
if (!relationship.endorsed) {
relationship.endorsed = true;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
endorsed: true,
},
});
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { and, eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,66 +32,40 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
if (foundRelationship.followedBy) {
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({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
if (otherUser.instanceId === null) {
// Also remove from followers list
await db
.update(relationship)
.set({
following: false,
})
.where(
and(
eq(relationship.ownerId, otherUser.id),
eq(relationship.subjectId, self.id),
),
);
}
}
if (relationship.followedBy) {
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));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,18 +1,12 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { sql } from "drizzle-orm";
import { client } from "~database/datasource";
import {
type StatusWithRelations,
findManyStatuses,
statusToAPI,
type StatusWithRelations,
} from "~database/entities/Status";
import { findFirstUser } from "~database/entities/User";
import {
statusAndUserRelations,
userRelations,
} from "~database/entities/relations";
export const meta = applyConfig({
allowedMethods: ["GET"],

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -20,9 +22,6 @@ export const meta = applyConfig({
},
});
/**
* Blocks a user
*/
export default apiRoute(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id;
@ -30,52 +29,24 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
if (foundRelationship.blocking) {
foundRelationship.blocking = false;
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
await db
.update(relationship)
.set({
blocking: false,
})
.where(eq(relationship.id, foundRelationship.id));
}
if (relationship.blocking) {
relationship.blocking = false;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
blocking: false,
},
});
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,53 +32,26 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
if (foundRelationship.following) {
foundRelationship.following = false;
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
await db
.update(relationship)
.set({
following: false,
requested: false,
})
.where(eq(relationship.id, foundRelationship.id));
}
if (relationship.following) {
relationship.following = false;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
following: false,
requested: false,
},
});
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,54 +32,27 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const user = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, user);
if (!relationship) {
// Create new relationship
if (foundRelationship.muting) {
foundRelationship.muting = false;
foundRelationship.mutingNotifications = false;
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
await db
.update(relationship)
.set({
muting: false,
mutingNotifications: false,
})
.where(eq(relationship.id, foundRelationship.id));
}
if (relationship.muting) {
relationship.muting = false;
}
// TODO: Implement duration
await client.relationship.update({
where: { id: relationship.id },
data: {
muting: false,
},
});
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,11 +1,13 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { eq } from "drizzle-orm";
import { relationshipToAPI } from "~database/entities/Relationship";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import { getRelationshipToOtherUser } from "~database/entities/User";
findFirstUser,
getRelationshipToOtherUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -30,52 +32,25 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!self) return errorResponse("Unauthorized", 401);
const user = await client.user.findUnique({
where: { id },
include: {
relationships: {
include: {
owner: true,
subject: true,
},
},
},
const otherUser = await findFirstUser({
where: (user, { eq }) => eq(user.id, id),
});
if (!user) return errorResponse("User not found", 404);
if (!otherUser) return errorResponse("User not found", 404);
// Check if already following
let relationship = await getRelationshipToOtherUser(self, user);
const foundRelationship = await getRelationshipToOtherUser(self, otherUser);
if (!relationship) {
// Create new relationship
if (foundRelationship.endorsed) {
foundRelationship.endorsed = false;
const newRelationship = await createNewRelationship(self, user);
await client.user.update({
where: { id: self.id },
data: {
relationships: {
connect: {
id: newRelationship.id,
},
},
},
});
relationship = newRelationship;
await db
.update(relationship)
.set({
endorsed: false,
})
.where(eq(relationship.id, foundRelationship.id));
}
if (relationship.endorsed) {
relationship.endorsed = false;
}
await client.relationship.update({
where: { id: relationship.id },
data: {
endorsed: false,
},
});
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,8 +1,7 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { userToAPI } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import { findManyUsers, userToAPI } from "~database/entities/User";
import { db } from "~drizzle/db";
export const meta = applyConfig({
allowedMethods: ["GET"],
@ -34,34 +33,52 @@ export default apiRoute<{
return errorResponse("Number of ids must be between 1 and 10", 422);
}
const followersOfIds = await client.user.findMany({
where: {
relationships: {
some: {
subjectId: {
in: ids,
},
following: true,
},
},
const idFollowerRelationships = await db.query.relationship.findMany({
columns: {
ownerId: true,
},
where: (relationship, { inArray, and, eq }) =>
and(
inArray(relationship.subjectId, ids),
eq(relationship.following, true),
),
});
// Find users that you follow in followersOfIds
const output = await client.user.findMany({
where: {
relationships: {
some: {
ownerId: self.id,
subjectId: {
in: followersOfIds.map((f) => f.id),
},
following: true,
},
},
if (idFollowerRelationships.length === 0) {
return jsonResponse([]);
}
// Find users that you follow in idFollowerRelationships
const relevantRelationships = await db.query.relationship.findMany({
columns: {
subjectId: 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)));
});

View file

@ -2,8 +2,7 @@ import { apiRoute, applyConfig } from "@api";
import { jsonResponse, response } from "@response";
import { tempmailDomains } from "@tempmail";
import ISO6391 from "iso-639-1";
import { client } from "~database/datasource";
import { createNewLocalUser } from "~database/entities/User";
import { createNewLocalUser, findFirstUser } from "~database/entities/User";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -127,11 +126,16 @@ export default apiRoute<{
});
// 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({
error: "ERR_TAKEN",
description: "is already taken",
});
}
// Check if email is valid
if (

View file

@ -1,15 +1,15 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { getUserUri } from "~database/entities/User";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} 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 { getUserUri } from "~database/entities/User";
import type { APIStatus } from "~types/entities/status";
import { meta } from "./index";
await deleteOldTestUsers();

View file

@ -1,11 +1,11 @@
import { apiRoute, applyConfig } from "@api";
import type { User } from "@prisma/client";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import {
createNewRelationship,
relationshipToAPI,
} from "~database/entities/Relationship";
import type { User } from "~database/entities/User";
import { db } from "~drizzle/db";
export const meta = applyConfig({
allowedMethods: ["GET"],
@ -37,13 +37,12 @@ export default apiRoute<{
return errorResponse("Number of ids must be between 1 and 10", 422);
}
const relationships = await client.relationship.findMany({
where: {
ownerId: self.id,
subjectId: {
in: ids,
},
},
const relationships = await db.query.relationship.findMany({
where: (relationship, { inArray, and, eq }) =>
and(
inArray(relationship.subjectId, ids),
eq(relationship.ownerId, self.id),
),
});
// Find IDs that dont have a relationship

View file

@ -1,15 +1,15 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { getUserUri } from "~database/entities/User";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} 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 { getUserUri } from "~database/entities/User";
import type { APIStatus } from "~types/entities/status";
import { meta } from "./index";
await deleteOldTestUsers();

View file

@ -2,10 +2,10 @@ import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { sql } from "drizzle-orm";
import {
type UserWithRelations,
findManyUsers,
resolveWebFinger,
userToAPI,
type UserWithRelations,
} from "~database/entities/User";
import { user } from "~drizzle/schema";

View file

@ -2,15 +2,16 @@ import { apiRoute, applyConfig } from "@api";
import { convertTextToHtml } from "@formatting";
import { errorResponse, jsonResponse } from "@response";
import { sanitizeHtml } from "@sanitization";
import { and, eq } from "drizzle-orm";
import ISO6391 from "iso-639-1";
import { MediaBackendType } 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 { parseEmojis } from "~database/entities/Emoji";
import { userToAPI } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import { S3MediaBackend, LocalMediaBackend } from "media-manager";
import { findFirstUser, userToAPI } from "~database/entities/User";
import { db } from "~drizzle/db";
import { emojiToUser, user } from "~drizzle/schema";
import type { APISource } from "~types/entities/source";
export const meta = applyConfig({
@ -38,9 +39,9 @@ export default apiRoute<{
"source[sensitive]": string;
"source[language]": string;
}>(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();
@ -109,12 +110,12 @@ export default apiRoute<{
}
// 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
if (sanitizedNote.length > config.validation.max_note_size) {
return errorResponse(
@ -128,12 +129,12 @@ export default apiRoute<{
return errorResponse("Bio contains blocked words", 422);
}
(user.source as APISource).note = sanitizedNote;
(self.source as APISource).note = sanitizedNote;
// 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
if (
!["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
if (source_sensitive !== "true" && source_sensitive !== "false") {
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)) {
return errorResponse(
"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) {
@ -180,7 +181,7 @@ export default apiRoute<{
const { path } = await mediaManager.addFile(avatar);
user.avatar = getUrl(path, config);
self.avatar = getUrl(path, config);
}
if (header) {
@ -194,7 +195,7 @@ export default apiRoute<{
const { path } = await mediaManager.addFile(header);
user.header = getUrl(path, config);
self.header = getUrl(path, config);
}
if (locked) {
@ -203,7 +204,7 @@ export default apiRoute<{
return errorResponse("Locked must be a boolean", 422);
}
user.isLocked = locked === "true";
self.isLocked = locked === "true";
}
if (bot) {
@ -212,7 +213,7 @@ export default apiRoute<{
return errorResponse("Bot must be a boolean", 422);
}
user.isBot = bot === "true";
self.isBot = bot === "true";
}
if (discoverable) {
@ -221,7 +222,7 @@ export default apiRoute<{
return errorResponse("Discoverable must be a boolean", 422);
}
user.isDiscoverable = discoverable === "true";
self.isDiscoverable = discoverable === "true";
}
// Parse emojis
@ -229,36 +230,49 @@ export default apiRoute<{
const displaynameEmojis = await parseEmojis(sanitizedDisplayName);
const noteEmojis = await parseEmojis(sanitizedNote);
user.emojis = [...displaynameEmojis, ...noteEmojis];
self.emojis = [...displaynameEmojis, ...noteEmojis];
// Deduplicate emojis
user.emojis = user.emojis.filter(
self.emojis = self.emojis.filter(
(emoji, index, self) =>
self.findIndex((e) => e.id === emoji.id) === index,
);
const output = await client.user.update({
where: { id: user.id },
data: {
displayName: user.displayName,
note: user.note,
avatar: user.avatar,
header: user.header,
isLocked: user.isLocked,
isBot: user.isBot,
isDiscoverable: user.isDiscoverable,
emojis: {
disconnect: user.emojis.map((e) => ({
id: e.id,
})),
connect: user.emojis.map((e) => ({
id: e.id,
})),
},
source: user.source || undefined,
},
include: userRelations,
await db
.update(user)
.set({
displayName: self.displayName,
note: self.note,
avatar: self.avatar,
header: self.header,
isLocked: self.isLocked,
isBot: self.isBot,
isDiscoverable: self.isDiscoverable,
source: self.source || undefined,
})
.where(eq(user.id, self.id));
// Connect emojis, if any
for (const emoji of self.emojis) {
await db
.delete(emojiToUser)
.where(and(eq(emojiToUser.a, emoji.id), eq(emojiToUser.b, self.id)))
.execute();
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));
});

View file

@ -1,7 +1,8 @@
import { randomBytes } from "node:crypto";
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { db } from "~drizzle/db";
import { application } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -33,24 +34,28 @@ export default apiRoute<{
return errorResponse("Redirect URI must be a valid URI", 422);
}
}
const application = await client.application.create({
data: {
name: client_name || "",
redirect_uris: redirect_uris || "",
scopes: scopes || "read",
website: website || null,
client_id: randomBytes(32).toString("base64url"),
secret: randomBytes(64).toString("base64url"),
},
});
const app = (
await db
.insert(application)
.values({
name: client_name || "",
redirectUris: redirect_uris || "",
scopes: scopes || "read",
website: website || null,
clientId: randomBytes(32).toString("base64url"),
secret: randomBytes(64).toString("base64url"),
})
.returning()
)[0];
return jsonResponse({
id: application.id,
name: application.name,
website: application.website,
client_id: application.client_id,
client_secret: application.secret,
redirect_uri: application.redirect_uris,
vapid_link: application.vapid_key,
id: app.id,
name: app.name,
website: app.website,
client_id: app.clientId,
client_secret: app.secret,
redirect_uri: app.redirectUris,
vapid_link: app.vapidKey,
});
});

View file

@ -2,9 +2,9 @@ import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import {
type UserWithRelations,
findManyUsers,
userToAPI,
type UserWithRelations,
} from "~database/entities/User";
export const meta = applyConfig({

View file

@ -1,6 +1,5 @@
import { apiRoute, applyConfig } from "@api";
import { jsonResponse } from "@response";
import { client } from "~database/datasource";
import { emojiToAPI } from "~database/entities/Emoji";
import { db } from "~drizzle/db";

View file

@ -1,12 +1,11 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import {
statusToAPI,
type StatusWithRelations,
findManyStatuses,
statusToAPI,
} from "~database/entities/Status";
import { statusAndUserRelations } from "~database/entities/relations";
export const meta = applyConfig({
allowedMethods: ["GET"],
@ -37,25 +36,18 @@ export default apiRoute<{
if (!user) return errorResponse("Unauthorized", 401);
const { objects, link } = await fetchTimeline<StatusWithRelations>(
client.status,
findManyStatuses,
{
where: {
id: {
lt: max_id ?? undefined,
gte: since_id ?? undefined,
gt: min_id ?? undefined,
},
likes: {
some: {
likerId: user.id,
},
},
},
include: statusAndUserRelations,
take: Number(limit),
orderBy: {
id: "desc",
},
// @ts-ignore
where: (status, { and, lt, gt, gte, eq, sql }) =>
and(
max_id ? lt(status.id, max_id) : undefined,
since_id ? gte(status.id, since_id) : undefined,
min_id ? gt(status.id, min_id) : undefined,
sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${status.id} AND "Like"."likerId" = ${user.id})`,
),
// @ts-expect-error Yes I KNOW the types are wrong
orderBy: (status, { desc }) => desc(status.id),
},
req,
);

View file

@ -1,12 +1,17 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { and, eq } from "drizzle-orm";
import {
checkForBidirectionalRelationships,
relationshipToAPI,
} from "~database/entities/Relationship";
import { sendFollowAccept } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import {
findFirstUser,
getRelationshipToOtherUser,
sendFollowAccept,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -27,11 +32,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
const { account_id } = matchedRoute.params;
const account = await client.user.findUnique({
where: {
id: account_id,
},
include: userRelations,
const account = await findFirstUser({
where: (user, { eq }) => eq(user.id, account_id),
});
if (!account) return errorResponse("Account not found", 404);
@ -40,37 +42,35 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
await checkForBidirectionalRelationships(user, account);
// Authorize follow request
await client.relationship.updateMany({
where: {
subjectId: user.id,
ownerId: account.id,
requested: true,
},
data: {
await db
.update(relationship)
.set({
requested: false,
following: true,
},
});
})
.where(
and(
eq(relationship.subjectId, user.id),
eq(relationship.ownerId, account.id),
),
);
// Update followedBy for other user
await client.relationship.updateMany({
where: {
subjectId: account.id,
ownerId: user.id,
},
data: {
await db
.update(relationship)
.set({
followedBy: true,
},
});
})
.where(
and(
eq(relationship.subjectId, account.id),
eq(relationship.ownerId, user.id),
),
);
const relationship = await client.relationship.findFirst({
where: {
subjectId: account.id,
ownerId: user.id,
},
});
const foundRelationship = await getRelationshipToOtherUser(user, account);
if (!relationship) return errorResponse("Relationship not found", 404);
if (!foundRelationship) return errorResponse("Relationship not found", 404);
// Check if accepting remote follow
if (account.instanceId) {
@ -78,5 +78,5 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
await sendFollowAccept(account, user);
}
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -1,12 +1,17 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { and, eq } from "drizzle-orm";
import {
checkForBidirectionalRelationships,
relationshipToAPI,
} from "~database/entities/Relationship";
import { sendFollowReject } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import {
findFirstUser,
getRelationshipToOtherUser,
sendFollowReject,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { relationship } from "~drizzle/schema";
export const meta = applyConfig({
allowedMethods: ["POST"],
@ -27,11 +32,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
const { account_id } = matchedRoute.params;
const account = await client.user.findUnique({
where: {
id: account_id,
},
include: userRelations,
const account = await findFirstUser({
where: (user, { eq }) => eq(user.id, account_id),
});
if (!account) return errorResponse("Account not found", 404);
@ -40,25 +42,35 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
await checkForBidirectionalRelationships(user, account);
// Reject follow request
await client.relationship.updateMany({
where: {
subjectId: user.id,
ownerId: account.id,
requested: true,
},
data: {
await db
.update(relationship)
.set({
requested: false,
},
});
following: false,
})
.where(
and(
eq(relationship.subjectId, user.id),
eq(relationship.ownerId, account.id),
),
);
const relationship = await client.relationship.findFirst({
where: {
subjectId: account.id,
ownerId: user.id,
},
});
// Update followedBy for other user
await db
.update(relationship)
.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
if (account.instanceId) {
@ -66,5 +78,5 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
await sendFollowReject(account, user);
}
return jsonResponse(relationshipToAPI(relationship));
return jsonResponse(relationshipToAPI(foundRelationship));
});

View file

@ -2,9 +2,9 @@ import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import {
type UserWithRelations,
findManyUsers,
userToAPI,
type UserWithRelations,
} from "~database/entities/User";
export const meta = applyConfig({

View file

@ -1,11 +1,9 @@
import { apiRoute, applyConfig } from "@api";
import { jsonResponse } from "@response";
import { count, isNull } from "drizzle-orm";
import { client } from "~database/datasource";
import { userToAPI } from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
import { and, count, countDistinct, eq, gte, isNull } from "drizzle-orm";
import { findFirstUser, userToAPI } from "~database/entities/User";
import { db } from "~drizzle/db";
import { status, user } from "~drizzle/schema";
import { instance, status, user } from "~drizzle/schema";
import manifest from "~package.json";
import type { APIInstance } from "~types/entities/instance";
@ -45,33 +43,39 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
.where(isNull(user.instanceId))
)[0].count;
// Get the first created admin user
const contactAccount = await client.user.findFirst({
where: {
instanceId: null,
isAdmin: true,
},
orderBy: {
id: "asc",
},
include: userRelations,
const contactAccount = await findFirstUser({
where: (user, { isNull, eq, and }) =>
and(isNull(user.instanceId), eq(user.isAdmin, true)),
orderBy: (user, { asc }) => asc(user.id),
});
// Get user that have posted once in the last 30 days
const monthlyActiveUsers = await client.user.count({
where: {
instanceId: null,
statuses: {
some: {
createdAt: {
gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
},
},
},
},
});
const monthlyActiveUsers = (
await db
.select({
count: countDistinct(user),
})
.from(user)
.leftJoin(status, eq(user.id, status.authorId))
.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
return jsonResponse({

View file

@ -1,9 +1,11 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse, response } from "@response";
import { eq } from "drizzle-orm";
import type { MediaBackend } from "media-manager";
import { MediaBackendType } from "media-manager";
import { client } from "~database/datasource";
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
import { db } from "~drizzle/db";
import { attachment } from "~drizzle/schema";
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
export const meta = applyConfig({
@ -35,13 +37,11 @@ export default apiRoute<{
const id = matchedRoute.params.id;
const attachment = await client.attachment.findUnique({
where: {
id,
},
const foundAttachment = await db.query.attachment.findFirst({
where: (attachment, { eq }) => eq(attachment.id, id),
});
if (!attachment) {
if (!foundAttachment) {
return errorResponse("Media not found", 404);
}
@ -49,15 +49,15 @@ export default apiRoute<{
switch (req.method) {
case "GET": {
if (attachment.url) {
return jsonResponse(attachmentToAPI(attachment));
if (foundAttachment.url) {
return jsonResponse(attachmentToAPI(foundAttachment));
}
return response(null, 206);
}
case "PUT": {
const { description, thumbnail } = extraData.parsedRequest;
let thumbnailUrl = attachment.thumbnail_url;
let thumbnailUrl = foundAttachment.thumbnailUrl;
let mediaManager: MediaBackend;
@ -78,26 +78,27 @@ export default apiRoute<{
thumbnailUrl = getUrl(path, config);
}
const descriptionText = description || attachment.description;
const descriptionText = description || foundAttachment.description;
if (
descriptionText !== attachment.description ||
thumbnailUrl !== attachment.thumbnail_url
descriptionText !== foundAttachment.description ||
thumbnailUrl !== foundAttachment.thumbnailUrl
) {
const newAttachment = await client.attachment.update({
where: {
id,
},
data: {
description: descriptionText,
thumbnail_url: thumbnailUrl,
},
});
const newAttachment = (
await db
.update(attachment)
.set({
description: descriptionText,
thumbnailUrl,
})
.where(eq(attachment.id, id))
.returning()
)[0];
return jsonResponse(attachmentToAPI(newAttachment));
}
return jsonResponse(attachmentToAPI(attachment));
return jsonResponse(attachmentToAPI(foundAttachment));
}
}

View file

@ -4,8 +4,9 @@ import { encode } from "blurhash";
import { MediaBackendType } from "media-manager";
import type { MediaBackend } from "media-manager";
import sharp from "sharp";
import { client } from "~database/datasource";
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
import { db } from "~drizzle/db";
import { attachment } from "~drizzle/schema";
import { LocalMediaBackend, S3MediaBackend } from "~packages/media-manager";
export const meta = applyConfig({
@ -132,20 +133,22 @@ export default apiRoute<{
thumbnailUrl = getUrl(path, config);
}
const newAttachment = await client.attachment.create({
data: {
url,
thumbnail_url: thumbnailUrl,
sha256: sha256.update(await file.arrayBuffer()).digest("hex"),
mime_type: file.type,
description: description ?? "",
size: file.size,
blurhash: blurhash ?? undefined,
width: metadata?.width ?? undefined,
height: metadata?.height ?? undefined,
},
});
const newAttachment = (
await db
.insert(attachment)
.values({
url,
thumbnailUrl,
sha256: sha256.update(await file.arrayBuffer()).digest("hex"),
mimeType: file.type,
description: description ?? "",
size: file.size,
blurhash: blurhash ?? undefined,
width: metadata?.width ?? undefined,
height: metadata?.height ?? undefined,
})
.returning()
)[0];
// TODO: Add job to process videos and other media
return jsonResponse(attachmentToAPI(newAttachment));

View file

@ -1,13 +1,11 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import {
type UserWithRelations,
findManyUsers,
userToAPI,
type UserWithRelations,
} from "~database/entities/User";
import { userRelations } from "~database/entities/relations";
export const meta = applyConfig({
allowedMethods: ["GET"],

View file

@ -1,20 +1,11 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import {
findManyNotifications,
notificationToAPI,
} from "~database/entities/Notification";
import {
statusAndUserRelations,
userRelations,
} from "~database/entities/relations";
import type {
Notification,
NotificationWithRelations,
} from "~database/entities/Notification";
import { db } from "~drizzle/db";
import type { NotificationWithRelations } from "~database/entities/Notification";
export const meta = applyConfig({
allowedMethods: ["GET"],

View file

@ -1,13 +1,12 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource";
import { createLike } from "~database/entities/Like";
import {
findFirstStatuses,
isViewableByUser,
statusToAPI,
} from "~database/entities/Status";
import { statusAndUserRelations } from "~database/entities/relations";
import { db } from "~drizzle/db";
import type { APIStatus } from "~types/entities/status";
export const meta = applyConfig({
@ -40,11 +39,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
if (!status || !isViewableByUser(status, user))
return errorResponse("Record not found", 404);
const existingLike = await client.like.findFirst({
where: {
likedId: status.id,
likerId: user.id,
},
const existingLike = await db.query.like.findFirst({
where: (like, { and, eq }) =>
and(eq(like.likedId, status.id), eq(like.likerId, user.id)),
});
if (!existingLike) {

View file

@ -1,14 +1,13 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} 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 { meta } from "./favourited_by";
await deleteOldTestUsers();
@ -21,7 +20,7 @@ afterAll(async () => {
beforeAll(async () => {
for (const status of timeline) {
await fetch(
const res = await fetch(
new URL(
`/api/v1/statuses/${status.id}/favourite`,
config.http.base_url,
@ -29,6 +28,7 @@ beforeAll(async () => {
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${tokens[1].accessToken}`,
},
},

View file

@ -3,9 +3,9 @@ import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
import {
type UserWithRelations,
findManyUsers,
userToAPI,
type UserWithRelations,
} from "~database/entities/User";
export const meta = applyConfig({

View file

@ -1,14 +1,14 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} 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 { APIStatus } from "~types/entities/status";
import { meta } from "./reblogged_by";
await deleteOldTestUsers();
@ -29,6 +29,7 @@ beforeAll(async () => {
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${tokens[1].accessToken}`,
},
},

View file

@ -4,8 +4,8 @@ import { fetchTimeline } from "@timelines";
import { findFirstStatuses, isViewableByUser } from "~database/entities/Status";
import {
type UserWithRelations,
userToAPI,
findManyUsers,
userToAPI,
} from "~database/entities/User";
export const meta = applyConfig({

View file

@ -1,12 +1,12 @@
import { afterAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestUsers,
sendTestRequest,
} from "~tests/utils";
import { config } from "~index";
import { meta } from "./index";
import type { APIStatus } from "~types/entities/status";
import { meta } from "./index";
await deleteOldTestUsers();

View file

@ -2,8 +2,6 @@ import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { sanitizeHtml } from "@sanitization";
import { parse } from "marked";
import { client } from "~database/datasource";
import { getFromToken } from "~database/entities/Application";
import type { StatusWithRelations } from "~database/entities/Status";
import {
createNewStatus,
@ -12,10 +10,7 @@ import {
parseTextMentions,
statusToAPI,
} from "~database/entities/Status";
import type { UserWithRelations } from "~database/entities/User";
import { statusAndUserRelations } from "~database/entities/relations";
import { db } from "~drizzle/db";
import type { APIStatus } from "~types/entities/status";
export const meta = applyConfig({
allowedMethods: ["POST"],

View file

@ -1,13 +1,13 @@
import { afterAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} from "~tests/utils";
import { config } from "~index";
import { meta } from "./home";
import type { APIStatus } from "~types/entities/status";
import { meta } from "./home";
await deleteOldTestUsers();

View file

@ -3,8 +3,8 @@ import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import {
type StatusWithRelations,
statusToAPI,
findManyStatuses,
statusToAPI,
} from "~database/entities/Status";
import { db } from "~drizzle/db";

View file

@ -1,13 +1,13 @@
import { afterAll, describe, expect, test } from "bun:test";
import { config } from "~index";
import {
deleteOldTestUsers,
getTestStatuses,
getTestUsers,
sendTestRequest,
} from "~tests/utils";
import { config } from "~index";
import { meta } from "./public";
import type { APIStatus } from "~types/entities/status";
import { meta } from "./public";
await deleteOldTestUsers();

View file

@ -3,9 +3,9 @@ import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource";
import {
type StatusWithRelations,
findManyStatuses,
statusToAPI,
type StatusWithRelations,
} from "~database/entities/Status";
import { statusAndUserRelations } from "~database/entities/relations";

View file

@ -1,10 +1,10 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import type * as Lysand from "lysand-types";
import { client } from "~database/datasource";
import { statusToLysand } from "~database/entities/Status";
import { userToLysand } from "~database/entities/User";
import { statusAndUserRelations } from "~database/entities/relations";
import type * as Lysand from "lysand-types";
import { statusToLysand } from "~database/entities/Status";
export const meta = applyConfig({
allowedMethods: ["GET"],

View file

@ -1,17 +1,17 @@
import { apiRoute, applyConfig } from "@api";
import { errorResponse, response } from "@response";
import { client } from "~database/datasource";
import { userRelations } from "~database/entities/relations";
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 type { APIStatus } from "~types/entities/status";
import {
followAcceptToLysand,
getRelationshipToOtherUser,
resolveUser,
sendFollowAccept,
} 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({
allowedMethods: ["POST"],

View file

@ -1,18 +1,18 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import type { Token } from "@prisma/client";
import { config } from "config-manager";
import { inArray } from "drizzle-orm";
import { client } from "~database/datasource";
import { TokenType } from "~database/entities/Token";
import {
type UserWithRelations,
createNewLocalUser,
} from "~database/entities/User";
import { db } from "~drizzle/db";
import { application, user } from "~drizzle/schema";
import type { APIEmoji } from "~types/entities/emoji";
import type { APIInstance } from "~types/entities/instance";
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;

View file

@ -1,14 +1,14 @@
import {
createNewLocalUser,
type User,
type UserWithRelations,
} from "~database/entities/User";
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 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

View file

@ -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 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>(
model: