mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
Compare commits
No commits in common. "7de4b573e3bc52b0e3b613d22011f8df2434fe12" and "59cd519337f9291af4cf35a4a403167bbea485f8" have entirely different histories.
7de4b573e3
...
59cd519337
4
bun.lock
4
bun.lock
|
|
@ -100,7 +100,6 @@
|
||||||
"altcha-lib": "catalog:",
|
"altcha-lib": "catalog:",
|
||||||
"bun-bagel": "catalog:",
|
"bun-bagel": "catalog:",
|
||||||
"chalk": "catalog:",
|
"chalk": "catalog:",
|
||||||
"confbox": "catalog:",
|
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"hono": "catalog:",
|
"hono": "catalog:",
|
||||||
"hono-openapi": "catalog:",
|
"hono-openapi": "catalog:",
|
||||||
|
|
@ -169,14 +168,12 @@
|
||||||
"@versia/client": "workspace:*",
|
"@versia/client": "workspace:*",
|
||||||
"@versia/sdk": "workspace:*",
|
"@versia/sdk": "workspace:*",
|
||||||
"altcha-lib": "catalog:",
|
"altcha-lib": "catalog:",
|
||||||
"bullmq": "catalog:",
|
|
||||||
"chalk": "catalog:",
|
"chalk": "catalog:",
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"hono": "catalog:",
|
"hono": "catalog:",
|
||||||
"hono-openapi": "catalog:",
|
"hono-openapi": "catalog:",
|
||||||
"html-to-text": "catalog:",
|
"html-to-text": "catalog:",
|
||||||
"ioredis": "catalog:",
|
"ioredis": "catalog:",
|
||||||
"ip-matching": "catalog:",
|
|
||||||
"linkify-html": "catalog:",
|
"linkify-html": "catalog:",
|
||||||
"magic-regexp": "catalog:",
|
"magic-regexp": "catalog:",
|
||||||
"markdown-it": "catalog:",
|
"markdown-it": "catalog:",
|
||||||
|
|
@ -185,7 +182,6 @@
|
||||||
"mitt": "catalog:",
|
"mitt": "catalog:",
|
||||||
"qs": "catalog:",
|
"qs": "catalog:",
|
||||||
"sharp": "catalog:",
|
"sharp": "catalog:",
|
||||||
"web-push": "catalog:",
|
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
"zod-to-json-schema": "catalog:",
|
"zod-to-json-schema": "catalog:",
|
||||||
"zod-validation-error": "catalog:",
|
"zod-validation-error": "catalog:",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { JSONObject } from "@versia/sdk";
|
import type { JSONObject } from "@versia/sdk";
|
||||||
import * as VersiaEntities from "@versia/sdk/entities";
|
import * as VersiaEntities from "@versia/sdk/entities";
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { User } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { User } from "../db/user.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export enum DeliveryJobType {
|
export enum DeliveryJobType {
|
||||||
FederateEntity = "federateEntity",
|
FederateEntity = "federateEntity",
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { Instance } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
|
import { Instances } from "@versia-server/kit/tables";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { Instance } from "../db/instance.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
import { Instances } from "../tables/schema.ts";
|
|
||||||
|
|
||||||
export enum FetchJobType {
|
export enum FetchJobType {
|
||||||
Instance = "instance",
|
Instance = "instance",
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import type { JSONObject } from "@versia/sdk";
|
import type { JSONObject } from "@versia/sdk";
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { ApiError } from "@versia-server/kit";
|
||||||
|
import { Instance, User } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import type { SocketAddress } from "bun";
|
import type { SocketAddress } from "bun";
|
||||||
import { ApiError } from "../api-error.ts";
|
import { InboxProcessor } from "../inbox/processor.ts";
|
||||||
import { Instance } from "../db/instance.ts";
|
|
||||||
import { User } from "../db/user.ts";
|
|
||||||
import { InboxProcessor } from "../inbox-processor.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export enum InboxJobType {
|
export enum InboxJobType {
|
||||||
ProcessEntity = "processEntity",
|
ProcessEntity = "processEntity",
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { Media } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import { calculateBlurhash } from "../../../classes/media/preprocessors/blurhash.ts";
|
import { calculateBlurhash } from "../media/preprocessors/blurhash.ts";
|
||||||
import { convertImage } from "../../../classes/media/preprocessors/image-conversion.ts";
|
import { convertImage } from "../media/preprocessors/image-conversion.ts";
|
||||||
import { Media } from "../db/media.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export enum MediaJobType {
|
export enum MediaJobType {
|
||||||
ConvertMedia = "convertMedia",
|
ConvertMedia = "convertMedia",
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { Note, PushSubscription, Token, User } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import { sendNotification } from "web-push";
|
import { sendNotification } from "web-push";
|
||||||
import { htmlToText } from "@/content_types.ts";
|
import { htmlToText } from "@/content_types.ts";
|
||||||
import { Note } from "../db/note.ts";
|
|
||||||
import { PushSubscription } from "../db/pushsubscription.ts";
|
|
||||||
import { Token } from "../db/token.ts";
|
|
||||||
import { User } from "../db/user.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export enum PushJobType {
|
export enum PushJobType {
|
||||||
Notify = "notify",
|
Notify = "notify",
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
|
import { Relationship, User } from "@versia-server/kit/db";
|
||||||
|
import { connection } from "@versia-server/kit/redis";
|
||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import { Relationship } from "../db/relationship.ts";
|
|
||||||
import { User } from "../db/user.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export enum RelationshipJobType {
|
export enum RelationshipJobType {
|
||||||
Unmute = "unmute",
|
Unmute = "unmute",
|
||||||
|
|
@ -6,7 +6,7 @@ import { versionPlugin } from "@clerc/plugin-version";
|
||||||
import { setupDatabase } from "@versia-server/kit/db";
|
import { setupDatabase } from "@versia-server/kit/db";
|
||||||
import { Clerc } from "clerc";
|
import { Clerc } from "clerc";
|
||||||
import { searchManager } from "~/classes/search/search-manager.ts";
|
import { searchManager } from "~/classes/search/search-manager.ts";
|
||||||
import pkg from "../package.json" with { type: "json" };
|
import pkg from "~/package.json" with { type: "json" };
|
||||||
import { rebuildIndexCommand } from "./index/rebuild.ts";
|
import { rebuildIndexCommand } from "./index/rebuild.ts";
|
||||||
import { refetchInstanceCommand } from "./instance/refetch.ts";
|
import { refetchInstanceCommand } from "./instance/refetch.ts";
|
||||||
import { createUserCommand } from "./user/create.ts";
|
import { createUserCommand } from "./user/create.ts";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { Instance } from "@versia-server/kit/db";
|
import { Instance } from "@versia-server/kit/db";
|
||||||
import { FetchJobType, fetchQueue } from "@versia-server/kit/queues/fetch";
|
|
||||||
import { Instances } from "@versia-server/kit/tables";
|
import { Instances } from "@versia-server/kit/tables";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
// @ts-expect-error - Root import is required or the Clec type definitions won't work
|
// @ts-expect-error - Root import is required or the Clec type definitions won't work
|
||||||
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
|
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
|
||||||
import { defineCommand, type Root } from "clerc";
|
import { defineCommand, type Root } from "clerc";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { FetchJobType, fetchQueue } from "~/classes/queues/fetch.ts";
|
||||||
|
|
||||||
export const refetchInstanceCommand = defineCommand(
|
export const refetchInstanceCommand = defineCommand(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ import { secureHeaders } from "hono/secure-headers";
|
||||||
import { openAPISpecs } from "hono-openapi";
|
import { openAPISpecs } from "hono-openapi";
|
||||||
import { Youch } from "youch";
|
import { Youch } from "youch";
|
||||||
import { applyToHono } from "@/bull-board.ts";
|
import { applyToHono } from "@/bull-board.ts";
|
||||||
import pkg from "../../package.json" with { type: "application/json" };
|
import pkg from "~/package.json" with { type: "application/json" };
|
||||||
|
import { PluginLoader } from "../../classes/plugin/loader.ts";
|
||||||
import type { ApiRouteExports, HonoEnv } from "../../types/api.ts";
|
import type { ApiRouteExports, HonoEnv } from "../../types/api.ts";
|
||||||
import { agentBans } from "./middlewares/agent-bans.ts";
|
import { agentBans } from "./middlewares/agent-bans.ts";
|
||||||
import { boundaryCheck } from "./middlewares/boundary-check.ts";
|
import { boundaryCheck } from "./middlewares/boundary-check.ts";
|
||||||
import { ipBans } from "./middlewares/ip-bans.ts";
|
import { ipBans } from "./middlewares/ip-bans.ts";
|
||||||
import { logger } from "./middlewares/logger.ts";
|
import { logger } from "./middlewares/logger.ts";
|
||||||
import { rateLimit } from "./middlewares/rate-limit.ts";
|
import { rateLimit } from "./middlewares/rate-limit.ts";
|
||||||
import { PluginLoader } from "./plugin-loader.ts";
|
|
||||||
import { routes } from "./routes.ts";
|
import { routes } from "./routes.ts";
|
||||||
// Extends Zod with OpenAPI schema generation
|
// Extends Zod with OpenAPI schema generation
|
||||||
import "zod-openapi/extend";
|
import "zod-openapi/extend";
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@
|
||||||
"magic-regexp": "catalog:",
|
"magic-regexp": "catalog:",
|
||||||
"altcha-lib": "catalog:",
|
"altcha-lib": "catalog:",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"zod-validation-error": "catalog:",
|
"zod-validation-error": "catalog:"
|
||||||
"confbox": "catalog:"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ import {
|
||||||
withUserParam,
|
withUserParam,
|
||||||
} from "@versia-server/kit/api";
|
} from "@versia-server/kit/api";
|
||||||
import { Relationship } from "@versia-server/kit/db";
|
import { Relationship } from "@versia-server/kit/db";
|
||||||
import {
|
|
||||||
RelationshipJobType,
|
|
||||||
relationshipQueue,
|
|
||||||
} from "@versia-server/kit/queues/relationships";
|
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver, validator } from "hono-openapi/zod";
|
import { resolver, validator } from "hono-openapi/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
RelationshipJobType,
|
||||||
|
relationshipQueue,
|
||||||
|
} from "~/classes/queues/relationships";
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.post(
|
app.post(
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { and, eq, isNull } from "drizzle-orm";
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver } from "hono-openapi/zod";
|
import { resolver } from "hono-openapi/zod";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import manifest from "../../../../../../package.json" with { type: "json" };
|
import manifest from "~/package.json" with { type: "json" };
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.get(
|
app.get(
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Users } from "@versia-server/kit/tables";
|
||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver } from "hono-openapi/zod";
|
import { resolver } from "hono-openapi/zod";
|
||||||
import pkg from "../../../../../../package.json" with { type: "json" };
|
import pkg from "~/package.json" with { type: "json" };
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.get(
|
app.get(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { apiRoute, handleZodError } from "@versia-server/kit/api";
|
import { apiRoute, handleZodError } from "@versia-server/kit/api";
|
||||||
import { InboxJobType, inboxQueue } from "@versia-server/kit/queues/inbox";
|
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { validator } from "hono-openapi/zod";
|
import { validator } from "hono-openapi/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { InboxJobType, inboxQueue } from "~/classes/queues/inbox";
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.post(
|
app.post(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { ApiError } from "@versia-server/kit";
|
import { ApiError } from "@versia-server/kit";
|
||||||
import { apiRoute, handleZodError } from "@versia-server/kit/api";
|
import { apiRoute, handleZodError } from "@versia-server/kit/api";
|
||||||
import { InboxJobType, inboxQueue } from "@versia-server/kit/queues/inbox";
|
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver, validator } from "hono-openapi/zod";
|
import { resolver, validator } from "hono-openapi/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { InboxJobType, inboxQueue } from "~/classes/queues/inbox";
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.post(
|
app.post(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Note, User } from "@versia-server/kit/db";
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver } from "hono-openapi/zod";
|
import { resolver } from "hono-openapi/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import manifest from "../../../../../../package.json" with { type: "json" };
|
import manifest from "~/package.json" with { type: "json" };
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.get(
|
app.get(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { asc } from "drizzle-orm";
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver } from "hono-openapi/zod";
|
import { resolver } from "hono-openapi/zod";
|
||||||
import { urlToContentFormat } from "@/content_types";
|
import { urlToContentFormat } from "@/content_types";
|
||||||
import pkg from "../../../../package.json" with { type: "json" };
|
import pkg from "~/package.json" with { type: "json" };
|
||||||
|
|
||||||
export default apiRoute((app) =>
|
export default apiRoute((app) =>
|
||||||
app.get(
|
app.get(
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import sharp from "sharp";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import { mimeLookup } from "@/content_types.ts";
|
import { mimeLookup } from "@/content_types.ts";
|
||||||
import { getMediaHash } from "../../../classes/media/media-hasher.ts";
|
import { getMediaHash } from "../../../classes/media/media-hasher.ts";
|
||||||
import { MediaJobType, mediaQueue } from "../queues/media.ts";
|
import { MediaJobType, mediaQueue } from "../../../classes/queues/media.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
|
|
||||||
type MediaType = InferSelectModel<typeof Medias>;
|
type MediaType = InferSelectModel<typeof Medias>;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,10 @@ import { createRegExp, exactly, global } from "magic-regexp";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import { mergeAndDeduplicate } from "@/lib.ts";
|
import { mergeAndDeduplicate } from "@/lib.ts";
|
||||||
import { sanitizedHtmlStrip } from "@/sanitization";
|
import { sanitizedHtmlStrip } from "@/sanitization";
|
||||||
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
|
import {
|
||||||
|
DeliveryJobType,
|
||||||
|
deliveryQueue,
|
||||||
|
} from "../../../classes/queues/delivery.ts";
|
||||||
import { Application } from "./application.ts";
|
import { Application } from "./application.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
import { Emoji } from "./emoji.ts";
|
import { Emoji } from "./emoji.ts";
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,11 @@ import { getBestContentType } from "@/content_types";
|
||||||
import { randomString } from "@/math";
|
import { randomString } from "@/math";
|
||||||
import { searchManager } from "~/classes/search/search-manager";
|
import { searchManager } from "~/classes/search/search-manager";
|
||||||
import type { HttpVerb, KnownEntity } from "~/types/api.ts";
|
import type { HttpVerb, KnownEntity } from "~/types/api.ts";
|
||||||
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
|
import {
|
||||||
import { PushJobType, pushQueue } from "../queues/push.ts";
|
DeliveryJobType,
|
||||||
|
deliveryQueue,
|
||||||
|
} from "../../../classes/queues/delivery.ts";
|
||||||
|
import { PushJobType, pushQueue } from "../../../classes/queues/push.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
import { Emoji } from "./emoji.ts";
|
import { Emoji } from "./emoji.ts";
|
||||||
import { Instance } from "./instance.ts";
|
import { Instance } from "./instance.ts";
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,7 @@
|
||||||
"markdown-it": "catalog:",
|
"markdown-it": "catalog:",
|
||||||
"markdown-it-toc-done-right": "catalog:",
|
"markdown-it-toc-done-right": "catalog:",
|
||||||
"markdown-it-container": "catalog:",
|
"markdown-it-container": "catalog:",
|
||||||
"@hackmd/markdown-it-task-lists": "catalog:",
|
"@hackmd/markdown-it-task-lists": "catalog:"
|
||||||
"bullmq": "catalog:",
|
|
||||||
"web-push": "catalog:",
|
|
||||||
"ip-matching": "catalog:"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"tables/migrations"
|
"tables/migrations"
|
||||||
|
|
@ -88,10 +85,6 @@
|
||||||
"import": "./regex.ts",
|
"import": "./regex.ts",
|
||||||
"default": "./regex.ts"
|
"default": "./regex.ts"
|
||||||
},
|
},
|
||||||
"./queues/*": {
|
|
||||||
"import": "./queues/*.ts",
|
|
||||||
"default": "./queues/*.ts"
|
|
||||||
},
|
|
||||||
"./markdown": {
|
"./markdown": {
|
||||||
"import": "./markdown.ts",
|
"import": "./markdown.ts",
|
||||||
"default": "./markdown.ts"
|
"default": "./markdown.ts"
|
||||||
|
|
|
||||||
|
|
@ -1,464 +0,0 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
||||||
import { randomUUIDv7 } from "bun";
|
|
||||||
import { setupDatabase, User } from "../db/index.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
import {
|
|
||||||
type EventTypes,
|
|
||||||
StreamingTimeline,
|
|
||||||
type TimelineTypes,
|
|
||||||
} from "./streaming.ts";
|
|
||||||
|
|
||||||
// Set up database and create test users
|
|
||||||
await setupDatabase();
|
|
||||||
|
|
||||||
const testUsers: User[] = [];
|
|
||||||
|
|
||||||
beforeAll(async (): Promise<void> => {
|
|
||||||
// Ensure Redis is connected
|
|
||||||
await connection.ping();
|
|
||||||
|
|
||||||
// Create test users*
|
|
||||||
// Can't use stuff from @versia-server/tests because it depends on this package
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
const user = await User.register(
|
|
||||||
`test-streaming-${randomUUIDv7().slice(0, 8)}`,
|
|
||||||
{
|
|
||||||
email: `test-streaming-${i}@example.com`,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
testUsers.push(user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async (): Promise<void> => {
|
|
||||||
// Delete test users
|
|
||||||
for (const user of testUsers) {
|
|
||||||
await user.delete();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("StreamingTimeline", (): void => {
|
|
||||||
test("should create timeline with user", (): void => {
|
|
||||||
const timeline = new StreamingTimeline("user", testUsers[0]);
|
|
||||||
|
|
||||||
expect(timeline.timeline).toBe("user");
|
|
||||||
expect(timeline.user).toBe(testUsers[0]);
|
|
||||||
expect(timeline.emitter).toBeDefined();
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should create timeline without user", (): void => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
|
|
||||||
expect(timeline.timeline).toBe("public");
|
|
||||||
expect(timeline.user).toBe(null);
|
|
||||||
expect(timeline.emitter).toBeDefined();
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should generate correct channel name for user timeline", (): void => {
|
|
||||||
const timeline = new StreamingTimeline("user", testUsers[0]);
|
|
||||||
|
|
||||||
// Access private property for testing
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
expect(channelName).toBe(`timeline:user:${testUsers[0].id}`);
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should generate correct channel name for public timeline", (): void => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
|
|
||||||
// Access private property for testing
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
expect(channelName).toBe("timeline:public");
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should emit event when publishing to same channel", async (): Promise<void> => {
|
|
||||||
const emitterTimeline = new StreamingTimeline("public");
|
|
||||||
const receiverTimeline = new StreamingTimeline("public");
|
|
||||||
let receivedType = "";
|
|
||||||
let receivedPayload: unknown = null;
|
|
||||||
|
|
||||||
// Listen for update events
|
|
||||||
receiverTimeline.emitter.on("update", (payload): void => {
|
|
||||||
receivedType = "update";
|
|
||||||
receivedPayload = payload;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait a bit for subscription to be established
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit an event
|
|
||||||
emitterTimeline.emitEvent("update", { id: "test-status-123" });
|
|
||||||
|
|
||||||
// Wait for event to be processed
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedType).toBe("update");
|
|
||||||
expect(receivedPayload).toEqual({ id: "test-status-123" });
|
|
||||||
|
|
||||||
emitterTimeline.close();
|
|
||||||
receiverTimeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should not emit event for different channel", async (): Promise<void> => {
|
|
||||||
const timeline1 = new StreamingTimeline("public");
|
|
||||||
const timeline2 = new StreamingTimeline("user", testUsers[0]);
|
|
||||||
|
|
||||||
let receivedEvent = false;
|
|
||||||
|
|
||||||
// Listen for events on timeline1
|
|
||||||
timeline1.emitter.on("update", (): void => {
|
|
||||||
receivedEvent = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait a bit for subscription to be established
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit event on timeline2 (different channel)
|
|
||||||
timeline2.emitEvent("update", { id: "test-status-123" });
|
|
||||||
|
|
||||||
// Wait for potential event processing
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvent).toBe(false);
|
|
||||||
|
|
||||||
timeline1.close();
|
|
||||||
timeline2.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle different event types", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
const receivedEvents: Array<{ type: string; payload: unknown }> = [];
|
|
||||||
|
|
||||||
// Listen for different event types
|
|
||||||
timeline.emitter.on("update", (payload): void => {
|
|
||||||
receivedEvents.push({ type: "update", payload });
|
|
||||||
});
|
|
||||||
timeline.emitter.on("delete", (payload): void => {
|
|
||||||
receivedEvents.push({ type: "delete", payload });
|
|
||||||
});
|
|
||||||
timeline.emitter.on("notification", (payload): void => {
|
|
||||||
receivedEvents.push({ type: "notification", payload });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for subscription
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit different events
|
|
||||||
timeline.emitEvent("update", { id: "status-1" });
|
|
||||||
timeline.emitEvent("delete", { id: "status-2" });
|
|
||||||
timeline.emitEvent("notification", { id: "notif-1" });
|
|
||||||
|
|
||||||
// Wait for events
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvents).toHaveLength(3);
|
|
||||||
expect(receivedEvents[0]).toEqual({
|
|
||||||
type: "update",
|
|
||||||
payload: { id: "status-1" },
|
|
||||||
});
|
|
||||||
expect(receivedEvents[1]).toEqual({
|
|
||||||
type: "delete",
|
|
||||||
payload: { id: "status-2" },
|
|
||||||
});
|
|
||||||
expect(receivedEvents[2]).toEqual({
|
|
||||||
type: "notification",
|
|
||||||
payload: { id: "notif-1" },
|
|
||||||
});
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle malformed JSON messages gracefully", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
|
|
||||||
let receivedEvent = false;
|
|
||||||
let warningLogged = false;
|
|
||||||
|
|
||||||
// Mock console.warn to capture warnings
|
|
||||||
const originalWarn = console.warn;
|
|
||||||
console.warn = (): void => {
|
|
||||||
warningLogged = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
timeline.emitter.on("update", (): void => {
|
|
||||||
receivedEvent = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for subscription
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish malformed JSON directly to Redis
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
await connection.publish(channelName, "invalid json");
|
|
||||||
|
|
||||||
// Wait for message processing
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvent).toBe(false);
|
|
||||||
expect(warningLogged).toBe(true);
|
|
||||||
|
|
||||||
// Restore console.warn
|
|
||||||
console.warn = originalWarn;
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle messages with missing type or payload", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
|
|
||||||
let receivedEvent = false;
|
|
||||||
|
|
||||||
timeline.emitter.on("update", (): void => {
|
|
||||||
receivedEvent = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for subscription
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish message with missing type
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
await connection.publish(
|
|
||||||
channelName,
|
|
||||||
JSON.stringify({ payload: { id: "test" } }),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Publish message with missing payload
|
|
||||||
await connection.publish(
|
|
||||||
channelName,
|
|
||||||
JSON.stringify({ type: "update" }),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait for message processing
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvent).toBe(false);
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should properly clean up resources on close", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
|
|
||||||
// Wait for subscription
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify subscription is active
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
expect(
|
|
||||||
timeline.redisConnection.listenerCount("message"),
|
|
||||||
).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
// Close timeline
|
|
||||||
timeline.close();
|
|
||||||
|
|
||||||
// Wait a bit
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Try to emit event - should not be received
|
|
||||||
let receivedEvent = false;
|
|
||||||
timeline.emitter.on("update", (): void => {
|
|
||||||
receivedEvent = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
await connection.publish(
|
|
||||||
channelName,
|
|
||||||
JSON.stringify({ type: "update", payload: { id: "test" } }),
|
|
||||||
);
|
|
||||||
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvent).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle multiple timelines with different channels", async (): Promise<void> => {
|
|
||||||
const timeline1 = new StreamingTimeline("public");
|
|
||||||
const timeline2 = new StreamingTimeline("user", testUsers[0]);
|
|
||||||
const timeline3 = new StreamingTimeline("user", testUsers[1]);
|
|
||||||
|
|
||||||
const events1: unknown[] = [];
|
|
||||||
const events2: unknown[] = [];
|
|
||||||
const events3: unknown[] = [];
|
|
||||||
|
|
||||||
timeline1.emitter.on("update", (payload): void => {
|
|
||||||
events1.push(payload);
|
|
||||||
});
|
|
||||||
timeline2.emitter.on("update", (payload): void => {
|
|
||||||
events2.push(payload);
|
|
||||||
});
|
|
||||||
timeline3.emitter.on("update", (payload): void => {
|
|
||||||
events3.push(payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for subscriptions
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 20);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit events to different timelines
|
|
||||||
timeline1.emitEvent("update", { id: "public-1" });
|
|
||||||
timeline2.emitEvent("update", { id: "user1-1" });
|
|
||||||
timeline3.emitEvent("update", { id: "user2-1" });
|
|
||||||
|
|
||||||
// Wait for events
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(events1).toEqual([{ id: "public-1" }]);
|
|
||||||
expect(events2).toEqual([{ id: "user1-1" }]);
|
|
||||||
expect(events3).toEqual([{ id: "user2-1" }]);
|
|
||||||
|
|
||||||
timeline1.close();
|
|
||||||
timeline2.close();
|
|
||||||
timeline3.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle filters_changed event with null payload", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
let receivedType = "";
|
|
||||||
let receivedPayload: unknown;
|
|
||||||
|
|
||||||
timeline.emitter.on("filters_changed", (payload): void => {
|
|
||||||
receivedType = "filters_changed";
|
|
||||||
receivedPayload = payload;
|
|
||||||
});
|
|
||||||
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
timeline.emitEvent("filters_changed", null);
|
|
||||||
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedType).toBe("filters_changed");
|
|
||||||
expect(receivedPayload).toBeNull();
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle all supported timeline types", (): void => {
|
|
||||||
const timelineTypes: TimelineTypes[] = [
|
|
||||||
"public",
|
|
||||||
"public:media",
|
|
||||||
"public:local",
|
|
||||||
"public:local:media",
|
|
||||||
"public:remote",
|
|
||||||
"public:remote:media",
|
|
||||||
"hashtag",
|
|
||||||
"hashtag:local",
|
|
||||||
"user",
|
|
||||||
"user:notification",
|
|
||||||
"list",
|
|
||||||
];
|
|
||||||
|
|
||||||
const timelines = timelineTypes.map(
|
|
||||||
(type) => new StreamingTimeline(type),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const [index, timeline] of timelines.entries()) {
|
|
||||||
expect(timeline.timeline).toBe(timelineTypes[index]);
|
|
||||||
const channelName = (timeline as unknown as { channelName: string })
|
|
||||||
.channelName;
|
|
||||||
expect(channelName).toBe(`timeline:${timelineTypes[index]}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
for (const timeline of timelines) {
|
|
||||||
timeline.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle all supported event types", async (): Promise<void> => {
|
|
||||||
const timeline = new StreamingTimeline("public");
|
|
||||||
const eventTypes: EventTypes[] = [
|
|
||||||
"update",
|
|
||||||
"delete",
|
|
||||||
"notification",
|
|
||||||
"filters_changed",
|
|
||||||
"announcement",
|
|
||||||
"announcement.reaction",
|
|
||||||
"announcement.delete",
|
|
||||||
"status.update",
|
|
||||||
];
|
|
||||||
|
|
||||||
const receivedEvents: Array<{ type: string; payload: unknown }> = [];
|
|
||||||
|
|
||||||
// Set up listeners for all event types
|
|
||||||
for (const eventType of eventTypes) {
|
|
||||||
timeline.emitter.on(eventType, (payload): void => {
|
|
||||||
receivedEvents.push({ type: eventType, payload });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit all event types
|
|
||||||
for (const [index, eventType] of eventTypes.entries()) {
|
|
||||||
const payload =
|
|
||||||
eventType === "filters_changed"
|
|
||||||
? null
|
|
||||||
: { id: `${eventType}-${index}` };
|
|
||||||
timeline.emitEvent(eventType, payload as never);
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise((resolve): void => {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(receivedEvents).toHaveLength(eventTypes.length);
|
|
||||||
for (const [index, eventType] of eventTypes.entries()) {
|
|
||||||
const expectedPayload =
|
|
||||||
eventType === "filters_changed"
|
|
||||||
? null
|
|
||||||
: { id: `${eventType}-${index}` };
|
|
||||||
expect(receivedEvents[index]).toEqual({
|
|
||||||
type: eventType,
|
|
||||||
payload: expectedPayload,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
timeline.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
/**
|
|
||||||
* Handles live updates to timelines using Redis to communicate between instances.
|
|
||||||
*
|
|
||||||
* Used in the Streaming API to push updates to clients in real-time.
|
|
||||||
* @see https://docs.joinmastodon.org/methods/streaming/#websocket
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { config } from "@versia-server/config";
|
|
||||||
import IORedis from "ioredis";
|
|
||||||
import mitt from "mitt";
|
|
||||||
import type { User } from "../db/user.ts";
|
|
||||||
import { connection } from "../redis.ts";
|
|
||||||
|
|
||||||
export type TimelineTypes =
|
|
||||||
| "public"
|
|
||||||
| "public:media"
|
|
||||||
| "public:local"
|
|
||||||
| "public:local:media"
|
|
||||||
| "public:remote"
|
|
||||||
| "public:remote:media"
|
|
||||||
| "hashtag"
|
|
||||||
| "hashtag:local"
|
|
||||||
| "user"
|
|
||||||
| "user:notification"
|
|
||||||
| "list";
|
|
||||||
|
|
||||||
export type EventTypes =
|
|
||||||
| "update"
|
|
||||||
| "delete"
|
|
||||||
| "notification"
|
|
||||||
| "filters_changed"
|
|
||||||
| "announcement"
|
|
||||||
| "announcement.reaction"
|
|
||||||
| "announcement.delete"
|
|
||||||
| "status.update";
|
|
||||||
|
|
||||||
export type EventPayloads = {
|
|
||||||
update: { id: string };
|
|
||||||
delete: { id: string };
|
|
||||||
notification: { id: string };
|
|
||||||
filters_changed: null;
|
|
||||||
announcement: { id: string };
|
|
||||||
"announcement.reaction": { id: string };
|
|
||||||
"announcement.delete": { id: string };
|
|
||||||
"status.update": { id: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
export class StreamingTimeline {
|
|
||||||
public readonly emitter =
|
|
||||||
mitt<{
|
|
||||||
[K in EventTypes]: EventPayloads[K];
|
|
||||||
}>();
|
|
||||||
public readonly redisConnection: IORedis;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
public readonly timeline: TimelineTypes,
|
|
||||||
public readonly user: User | null = null,
|
|
||||||
) {
|
|
||||||
this.redisConnection = new IORedis({
|
|
||||||
host: config.redis.queue.host,
|
|
||||||
port: config.redis.queue.port,
|
|
||||||
password: config.redis.queue.password,
|
|
||||||
db: config.redis.queue.database,
|
|
||||||
maxRetriesPerRequest: null,
|
|
||||||
});
|
|
||||||
this.initializeRedisWatcher();
|
|
||||||
}
|
|
||||||
|
|
||||||
private get channelName(): string {
|
|
||||||
if (this.user) {
|
|
||||||
return `timeline:${this.timeline}:${this.user.id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `timeline:${this.timeline}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private messageHandler = (channel: string, message: string): void => {
|
|
||||||
if (channel === this.channelName) {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(message);
|
|
||||||
if (
|
|
||||||
typeof parsed === "object" &&
|
|
||||||
parsed !== null &&
|
|
||||||
"type" in parsed &&
|
|
||||||
"payload" in parsed
|
|
||||||
) {
|
|
||||||
const { type, payload } = parsed as {
|
|
||||||
type: EventTypes;
|
|
||||||
payload: unknown;
|
|
||||||
};
|
|
||||||
this.emitter.emit(
|
|
||||||
type,
|
|
||||||
payload as EventPayloads[typeof type],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Silently ignore malformed messages
|
|
||||||
console.warn("Failed to parse streaming message:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private initializeRedisWatcher(): void {
|
|
||||||
this.redisConnection.subscribe(this.channelName);
|
|
||||||
this.redisConnection.on("message", this.messageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public close(): void {
|
|
||||||
this.redisConnection.unsubscribe(this.channelName);
|
|
||||||
this.redisConnection.off("message", this.messageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public emitEvent<K extends EventTypes>(
|
|
||||||
type: K,
|
|
||||||
payload: EventPayloads[K],
|
|
||||||
): void {
|
|
||||||
connection.publish(this.channelName, JSON.stringify({ type, payload }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { getDeliveryWorker } from "@versia-server/kit/queues/delivery";
|
import { getDeliveryWorker } from "~/classes/queues/delivery";
|
||||||
import { getFetchWorker } from "@versia-server/kit/queues/fetch";
|
import { getFetchWorker } from "~/classes/queues/fetch";
|
||||||
import { getInboxWorker } from "@versia-server/kit/queues/inbox";
|
import { getInboxWorker } from "~/classes/queues/inbox";
|
||||||
import { getMediaWorker } from "@versia-server/kit/queues/media";
|
import { getMediaWorker } from "~/classes/queues/media";
|
||||||
import { getPushWorker } from "@versia-server/kit/queues/push";
|
import { getPushWorker } from "~/classes/queues/push";
|
||||||
import { getRelationshipWorker } from "@versia-server/kit/queues/relationships";
|
import { getRelationshipWorker } from "~/classes/queues/relationships";
|
||||||
|
|
||||||
export const workers = {
|
export const workers = {
|
||||||
fetch: getFetchWorker,
|
fetch: getFetchWorker,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { and, eq, type SQL } from "drizzle-orm";
|
||||||
import { describeRoute } from "hono-openapi";
|
import { describeRoute } from "hono-openapi";
|
||||||
import { resolver, validator } from "hono-openapi/zod";
|
import { resolver, validator } from "hono-openapi/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import type { PluginType } from "../../../index.ts";
|
import type { PluginType } from "~/plugins/openid";
|
||||||
|
|
||||||
export default (plugin: PluginType): void => {
|
export default (plugin: PluginType): void => {
|
||||||
plugin.registerRoute("/api/v1/sso/:id", (app) => {
|
plugin.registerRoute("/api/v1/sso/:id", (app) => {
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@ import { createBullBoard } from "@bull-board/api";
|
||||||
import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
|
import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
|
||||||
import { HonoAdapter } from "@bull-board/hono";
|
import { HonoAdapter } from "@bull-board/hono";
|
||||||
import { config } from "@versia-server/config";
|
import { config } from "@versia-server/config";
|
||||||
import { deliveryQueue } from "@versia-server/kit/queues/delivery";
|
|
||||||
import { fetchQueue } from "@versia-server/kit/queues/fetch";
|
|
||||||
import { inboxQueue } from "@versia-server/kit/queues/inbox";
|
|
||||||
import { mediaQueue } from "@versia-server/kit/queues/media";
|
|
||||||
import { pushQueue } from "@versia-server/kit/queues/push";
|
|
||||||
import { relationshipQueue } from "@versia-server/kit/queues/relationships";
|
|
||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import { serveStatic } from "hono/bun";
|
import { serveStatic } from "hono/bun";
|
||||||
|
import { deliveryQueue } from "~/classes/queues/delivery";
|
||||||
|
import { fetchQueue } from "~/classes/queues/fetch";
|
||||||
|
import { inboxQueue } from "~/classes/queues/inbox";
|
||||||
|
import { mediaQueue } from "~/classes/queues/media";
|
||||||
|
import { pushQueue } from "~/classes/queues/push";
|
||||||
|
import { relationshipQueue } from "~/classes/queues/relationships";
|
||||||
|
import pkg from "~/package.json" with { type: "json" };
|
||||||
import type { HonoEnv } from "~/types/api";
|
import type { HonoEnv } from "~/types/api";
|
||||||
import pkg from "../package.json" with { type: "json" };
|
|
||||||
|
|
||||||
export const applyToHono = (app: Hono<HonoEnv>): void => {
|
export const applyToHono = (app: Hono<HonoEnv>): void => {
|
||||||
const serverAdapter = new HonoAdapter(serveStatic);
|
const serverAdapter = new HonoAdapter(serveStatic);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue