mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
feat(api): ✨ Implement /api/v2/instance route
This commit is contained in:
parent
505273e064
commit
fe71f3628c
32
server/api/api/v2/instance/index.test.ts
Normal file
32
server/api/api/v2/instance/index.test.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { meta } from "./index";
|
||||||
|
import { sendTestRequest } from "~tests/utils";
|
||||||
|
import { config } from "config-manager";
|
||||||
|
|
||||||
|
// /api/v2/instance
|
||||||
|
describe(meta.route, () => {
|
||||||
|
test("should return instance information", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(new URL(meta.route, config.http.base_url)),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
expect(json).toBeObject();
|
||||||
|
expect(json).toContainKeys([
|
||||||
|
"domain",
|
||||||
|
"title",
|
||||||
|
"version",
|
||||||
|
"source_url",
|
||||||
|
"description",
|
||||||
|
"usage",
|
||||||
|
"thumbnail",
|
||||||
|
"languages",
|
||||||
|
"configuration",
|
||||||
|
"registrations",
|
||||||
|
"contact",
|
||||||
|
"rules",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
113
server/api/api/v2/instance/index.ts
Normal file
113
server/api/api/v2/instance/index.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { apiRoute, applyConfig } from "@api";
|
||||||
|
import { jsonResponse } from "@response";
|
||||||
|
import { and, count, countDistinct, eq, gte, isNull } from "drizzle-orm";
|
||||||
|
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { instance, status, user } from "~drizzle/schema";
|
||||||
|
import manifest from "~package.json";
|
||||||
|
import type { Instance as APIInstance } from "~types/mastodon/instance";
|
||||||
|
|
||||||
|
export const meta = applyConfig({
|
||||||
|
allowedMethods: ["GET"],
|
||||||
|
route: "/api/v2/instance",
|
||||||
|
ratelimits: {
|
||||||
|
max: 300,
|
||||||
|
duration: 60,
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||||
|
const config = await extraData.configManager.getConfig();
|
||||||
|
|
||||||
|
// Get software version from package.json
|
||||||
|
const version = manifest.version;
|
||||||
|
const contactAccount = await findFirstUser({
|
||||||
|
where: (user, { isNull, eq, and }) =>
|
||||||
|
and(isNull(user.instanceId), eq(user.isAdmin, true)),
|
||||||
|
orderBy: (user, { asc }) => asc(user.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// TODO: fill in more values
|
||||||
|
return jsonResponse({
|
||||||
|
domain: new URL(config.http.base_url).hostname,
|
||||||
|
title: config.instance.name,
|
||||||
|
version: `4.3.0+glitch (compatible; Lysand ${version}})`,
|
||||||
|
source_url: "https://github.com/lysand-org/lysand",
|
||||||
|
description: config.instance.description,
|
||||||
|
usage: {
|
||||||
|
users: {
|
||||||
|
active_month: monthlyActiveUsers,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
thumbnail: {
|
||||||
|
url: config.instance.logo,
|
||||||
|
},
|
||||||
|
languages: ["en"],
|
||||||
|
configuration: {
|
||||||
|
urls: {
|
||||||
|
streaming: null,
|
||||||
|
status: null,
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
max_featured_tags: 100,
|
||||||
|
},
|
||||||
|
statuses: {
|
||||||
|
max_characters: config.validation.max_note_size,
|
||||||
|
max_media_attachments: config.validation.max_media_attachments,
|
||||||
|
characters_reserved_per_url: 0,
|
||||||
|
},
|
||||||
|
media_attachments: {
|
||||||
|
supported_mime_types: config.validation.allowed_mime_types,
|
||||||
|
image_size_limit: config.validation.max_media_size,
|
||||||
|
image_matrix_limit: config.validation.max_media_size,
|
||||||
|
video_size_limit: config.validation.max_media_size,
|
||||||
|
video_frame_rate_limit: config.validation.max_media_size,
|
||||||
|
video_matrix_limit: config.validation.max_media_size,
|
||||||
|
},
|
||||||
|
polls: {
|
||||||
|
max_characters_per_option:
|
||||||
|
config.validation.max_poll_option_size,
|
||||||
|
max_expiration: config.validation.max_poll_duration,
|
||||||
|
max_options: config.validation.max_poll_options,
|
||||||
|
min_expiration: config.validation.min_poll_duration,
|
||||||
|
},
|
||||||
|
translation: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
registrations: {
|
||||||
|
enabled: config.signups.registration,
|
||||||
|
approval_required: false,
|
||||||
|
message: null,
|
||||||
|
url: null,
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
email: contactAccount?.email || null,
|
||||||
|
account: contactAccount ? userToAPI(contactAccount) : null,
|
||||||
|
},
|
||||||
|
rules: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue