diff --git a/README.md b/README.md index 70a6a59..5f3a755 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,6 @@ Consider all environment variables required. - Formatting: `pnpm format` - Linting (if you don't have an ESLint plugin): `pnpm format` -## Creating migrations - -Creating migrations is a little awkward because TypeORM expects `ts-node`, which uses `tsc`. -To create a migration, first run `pnpm build`, -then run `pnpm typeorm migration:generate -p -d ./dist/db/index.js ./src/db/migrations/`, -replacing `` with the name of the migration you're creating. -Then rename the created file to end in `.js` and remove the TypeScript-specific code from it. -(Yes, this is incredibly janky, but it works, and it only needs to be done once per migration, actually migrating works flawlessly) - ## License Mercury is licensed under the GNU Affero General Public License, version 3 **only**. diff --git a/package.json b/package.json index 0c941ea..9698dbb 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "watch:dev": "nodemon --watch \"dist/**/*\" -e js ./dist/index.js", "format": "prettier -w", "lint": "eslint src/", - "typeorm": "typeorm", - "migrate": "pnpm build && typeorm migration:run -d ./dist/db/index.js" + "typeorm": "typeorm-ts-node-esm", + "migrate": "typeorm-ts-node-esm migration:run -d ./src/db/index.ts" }, "dependencies": { "argon2": "^0.30.3", diff --git a/src/ap/blog.ts b/src/ap/blog.ts deleted file mode 100644 index 8a4f56f..0000000 --- a/src/ap/blog.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BASE_URL } from "~/config.js"; -import { Blog } from "~/db/entities/blog.js"; - -/** Transforms the given Blog into an ActivityPub Person. It is the caller's responsibility to ensure the blog is local. */ -export function blogToActivityPub(blog: Blog) { - return { - "@context": "https://www.w3.org/ns/activitystreams", - type: "Person", - id: `${BASE_URL}/blogs/${blog.username}`, - inbox: `${BASE_URL}/blogs/${blog.username}/inbox`, - outbox: `${BASE_URL}/blogs/${blog.username}/outbox`, - name: blog.username, - preferredUsername: blog.username, - publicKey: { - id: `${BASE_URL}/blogs/${blog.username}#main-key`, - owner: `${BASE_URL}/blogs/${blog.username}`, - publicKeyPem: blog.publicKey, - }, - }; -} diff --git a/src/db/blog.ts b/src/db/blog.ts index c23ccc6..64bc8ba 100644 --- a/src/db/blog.ts +++ b/src/db/blog.ts @@ -5,11 +5,11 @@ import { Blog } from "~entities/blog.js"; import { generateKeyPair } from "./util/rsa.js"; import MercuryDataSource from "./index.js"; -export const incorrectAccountType = new Error("wrong account type for function"); +export const notLocalAccount = new Error("account is not local"); /** Create a local blog. Throws an error if the given account is not a local account. */ export async function createLocalBlog(account: Account, name: string) { - if (account.host) throw incorrectAccountType; + if (account.host) throw notLocalAccount; const keyPair = await generateKeyPair(); @@ -25,19 +25,3 @@ export async function createLocalBlog(account: Account, name: string) { return blog; } - -export async function createRemoteBlog(account: Account, name: string, apId: string, publicKey: string) { - if (!account.host) throw incorrectAccountType; - - const blog = new Blog(); - blog.id = ulid(); - blog.apId = apId; - blog.username = name; - blog.host = account.host; - blog.account = account; - blog.publicKey = publicKey; - - await MercuryDataSource.getRepository(Blog).save(blog); - - return blog; -} diff --git a/src/db/entities/blog.ts b/src/db/entities/blog.ts index daef6c0..e92f03f 100644 --- a/src/db/entities/blog.ts +++ b/src/db/entities/blog.ts @@ -14,8 +14,6 @@ import { Post } from "./post.js"; export class Blog { @PrimaryColumn("text") id: string; - @Column("text", { nullable: true, unique: true, comment: "ActivityPub ID" }) - apId: string | null; @Column("text", { nullable: false }) username: string; @Column("text", { nullable: true }) diff --git a/src/db/entities/post.ts b/src/db/entities/post.ts index 10a1e38..4cc665b 100644 --- a/src/db/entities/post.ts +++ b/src/db/entities/post.ts @@ -5,8 +5,6 @@ import { Blog } from "./blog.js"; export class Post { @PrimaryColumn("text") id: string; - @Column("text", { nullable: true, unique: true, comment: "ActivityPub ID" }) - apId: string | null; @Column("text", { nullable: true }) content: string | null; @Column("text", { nullable: true }) diff --git a/src/db/index.ts b/src/db/index.ts index 5c202e3..2e22177 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -15,8 +15,6 @@ const MercuryDataSource = new DataSource({ database: config.DATABASE_NAME, entities: [Account, Blog, Post], migrations: ["src/db/migrations/*.js"], - logging: - process.env.NODE_ENV === "production" ? ["error"] : ["query", "error"], }); export default MercuryDataSource; diff --git a/src/db/migrations/1689900517279-addApIds.js b/src/db/migrations/1689900517279-addApIds.js deleted file mode 100644 index 493a336..0000000 --- a/src/db/migrations/1689900517279-addApIds.js +++ /dev/null @@ -1,50 +0,0 @@ -export class AddApIds1689900517279 { - name = 'AddApIds1689900517279' - - async up(queryRunner) { - await queryRunner.query(` - ALTER TABLE "post" - ADD "apId" text - `); - await queryRunner.query(` - ALTER TABLE "post" - ADD CONSTRAINT "UQ_e16e967a725a0f3f681bf99bd6e" UNIQUE ("apId") - `); - await queryRunner.query(` - COMMENT ON COLUMN "post"."apId" IS 'ActivityPub ID' - `); - await queryRunner.query(` - ALTER TABLE "blog" - ADD "apId" text - `); - await queryRunner.query(` - ALTER TABLE "blog" - ADD CONSTRAINT "UQ_624066bd60ecf91ee390637d171" UNIQUE ("apId") - `); - await queryRunner.query(` - COMMENT ON COLUMN "blog"."apId" IS 'ActivityPub ID' - `); - } - - async down(queryRunner) { - await queryRunner.query(` - COMMENT ON COLUMN "blog"."apId" IS 'ActivityPub ID' - `); - await queryRunner.query(` - ALTER TABLE "blog" DROP CONSTRAINT "UQ_624066bd60ecf91ee390637d171" - `); - await queryRunner.query(` - ALTER TABLE "blog" DROP COLUMN "apId" - `); - await queryRunner.query(` - COMMENT ON COLUMN "post"."apId" IS 'ActivityPub ID' - `); - await queryRunner.query(` - ALTER TABLE "post" DROP CONSTRAINT "UQ_e16e967a725a0f3f681bf99bd6e" - `); - await queryRunner.query(` - ALTER TABLE "post" DROP COLUMN "apId" - `); - } - -} diff --git a/src/routes/ap/blog.ts b/src/routes/ap/blog.ts deleted file mode 100644 index 3dd13b0..0000000 --- a/src/routes/ap/blog.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { RouteOptions } from "fastify"; - -import MercuryDataSource from "~/db/index.js"; -import { Blog } from "~/db/entities/blog.js"; -import { IsNull } from "typeorm"; -import { blogToActivityPub } from "~/ap/blog.js"; - -const route: RouteOptions = { - method: "GET", - url: "/blogs/:username", - handler: async (req, res) => { - const username = (req.params as { username: string }).username; - - // Only respond to ActivityPub requests - if (req.headers.accept && !req.headers.accept.indexOf("application/json")) { - res.redirect(303, `/@${username}`); - } - - const blog = await MercuryDataSource.getRepository(Blog).findOneBy({ - username, - host: IsNull(), - }); - if (!blog) { - return res.status(404).send({ message: "Not found" }); - } - - return res.send(blogToActivityPub(blog)); - }, -}; - -export default route; diff --git a/src/routes/nodeinfo/nodeinfo_2.0.ts b/src/routes/nodeinfo/nodeinfo_2.0.ts deleted file mode 100644 index 122a60b..0000000 --- a/src/routes/nodeinfo/nodeinfo_2.0.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { RouteOptions } from "fastify"; -import { IsNull } from "typeorm"; - -import MercuryDataSource from "~/db/index.js"; -import { Blog } from "~/db/entities/blog.js"; -import { Post } from "~/db/entities/post.js"; - -const route: RouteOptions = { - method: "GET", - url: "/nodeinfo/2.0", - handler: async (_, res) => { - const [userCount, postCount] = await Promise.all([ - MercuryDataSource.getRepository(Blog).countBy({ - host: IsNull(), - }), - MercuryDataSource.getRepository(Post).count({ - relations: { blog: true }, - where: { blog: { host: IsNull() } }, - }), - ]); - - res.send({ - version: "2.0", - software: { name: "mercury", version: "0.1.0-dev" }, - protocols: ["activitypub"], - openRegistrations: false, // TODO: get from database - usage: { - users: { - total: userCount, - }, - localPosts: postCount, - }, - }); - }, -}; - -export default route; diff --git a/src/routes/well-known/nodeinfo.ts b/src/routes/well-known/nodeinfo.ts deleted file mode 100644 index 9eeb672..0000000 --- a/src/routes/well-known/nodeinfo.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RouteOptions } from "fastify"; - -import { BASE_URL } from "~/config.js"; - -const route: RouteOptions = { - method: "GET", - url: "/.well-known/nodeinfo", - handler: async (_, res) => { - res.send({ - links: [ - { - href: `${BASE_URL}/nodeinfo/2.0`, - rel: "http://nodeinfo.diaspora.software/ns/schema/2.0", - }, - ], - }); - }, -}; - -export default route; diff --git a/src/start.ts b/src/start.ts index d3da7fe..c20d5b5 100644 --- a/src/start.ts +++ b/src/start.ts @@ -21,11 +21,6 @@ export default async function start() { log.debug("Setting up routes"); const app = Fastify(); - app.setNotFoundHandler((req, res) => { - log.debug("Route %s not found", req.url); - res.status(404).send({ message: "Not found" }); - }); - const routes = await getRoutes(); mountRoutes(app, routes); diff --git a/tsconfig.json b/tsconfig.json index 01f9e71..315db74 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,5 @@ "~/*": ["./src/*"], "~entities/*": ["./src/db/entities/*"] } - }, - "exclude": ["./dist/"] + } }