Removed NodeInfo and WebFinger endpoints

This commit is contained in:
Natty 2023-04-22 12:47:12 +02:00
parent 86c4804f9b
commit ddf7e07481
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
6 changed files with 163 additions and 551 deletions

View File

@ -1,10 +1,7 @@
root = true root = true
[*] [*]
indent_style = tab indent_style = space
indent_size = 2 indent_size = 4
charset = utf-8 charset = utf-8
insert_final_newline = true insert_final_newline = true
[*.yml]
indent_style = space

View File

@ -1,72 +0,0 @@
# Replace example.tld with your domain
# For WebSocket
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off;
server {
listen 80;
listen [::]:80;
server_name example.tld;
# For SSL domain validation
root /var/www/html;
location /.well-known/acme-challenge/ { allow all; }
location /.well-known/pki-validation/ { allow all; }
location / { return 301 https://$server_name$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.tld;
ssl_session_timeout 1d;
ssl_session_cache shared:ssl_session_cache:10m;
ssl_session_tickets off;
# To use Let's Encrypt certificate
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
# To use Debian/Ubuntu's self-signed certificate (For testing or before issuing a certificate)
#ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
#ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
# SSL protocol settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_stapling on;
ssl_stapling_verify on;
# Change to your upload limit
client_max_body_size 80m;
# Proxy to Node
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_redirect off;
# If it's behind another reverse proxy or CDN, remove the following.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# For WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Cache settings
proxy_cache cache1;
proxy_cache_lock on;
proxy_cache_use_stale updating;
add_header X-Cache $upstream_cache_status;
}
}

View File

@ -1,103 +0,0 @@
# configuration file for git-cliff (0.1.0)
[changelog]
# changelog header
header = """
# Changelog\n
All changes from v13.0.0 onwards, for a full list of differences read CALCKEY.md\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = false
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/orhun/git-cliff/issues/${2}))"},
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features"},
{ message = "^add", group = "Features"},
{ message = "^fix", group = "Bug Fixes"},
{ message = "^prevent", group = "Bug Fixes"},
{ message = "^doc", group = "Documentation"},
{ message = "^perf", group = "Performance"},
{ message = "^🎨", group = "Refactor"},
{ message = "^enhance", group = "Refactor"},
{ message = "^⚡️", group = "Refactor"},
{ message = "^🔥", group = "Features"},
{ message = "^🐛", group = "Bug Fixes"},
{ message = "^🚑️", group = "Bug Fixes"},
{ message = "^block", group = "Bug Fixes"},
{ message = "^✨", group = "Features"},
{ message = "^📝", group = "Documentation"},
{ message = "^🚀", group = "Features"},
{ message = "^💄", group = "Styling"},
{ message = "^✅", group = "Testing"},
{ message = "^🔒️", group = "Security"},
{ message = "^🚨", group = "Testing"},
{ message = "^💚", group = "CI"},
{ message = "^👷", group = "CI"},
{ message = "^⬇️", group = "Miscellaneous Tasks"},
{ message = "^⬆️", group = "Miscellaneous Tasks"},
{ message = "^📌", group = "Miscellaneous Tasks"},
{ message = "^", group = "Miscellaneous Tasks"},
{ message = "^", group = "Miscellaneous Tasks"},
{ message = "^♻️", group = "Refactor"},
{ message = "^🔧", group = "CI"},
{ message = "^🔨", group = "CI"},
{ message = "^🌐", group = "Localization"},
{ message = "^✏️", group = "Localization"},
{ message = "^👽️", group = "Bug Fixes"},
{ message = "^🍱", group = "Styling"},
{ message = "^♿️", group = "Styling"},
{ message = "^🩹", group = "Bug Fixes"},
{ message = "^refactor", group = "Refactor"},
{ message = "^style", group = "Styling"},
{ message = "^test", group = "Testing"},
{ message = "^chore\\(release\\): prepare for", skip = true},
{ message = "^chore", group = "Miscellaneous Tasks"},
{ message = "^update", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]*"
# regex for skipping tags
skip_tags = "v0.1.0-beta.1"
# regex for ignoring tags
ignore_tags = ""
# sort the tags chronologically
date_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"
# limit the number of commits included in the changelog.
# limit_commits = 42

View File

@ -21,7 +21,6 @@ import { publishMainStream } from "@/services/stream.js";
import * as Acct from "@/misc/acct.js"; import * as Acct from "@/misc/acct.js";
import { envOption } from "@/env.js"; import { envOption } from "@/env.js";
import activityPub from "./activitypub.js"; import activityPub from "./activitypub.js";
import nodeinfo from "./nodeinfo.js";
import wellKnown from "./well-known.js"; import wellKnown from "./well-known.js";
import apiServer from "./api/index.js"; import apiServer from "./api/index.js";
import fileServer from "./file/index.js"; import fileServer from "./file/index.js";
@ -40,7 +39,7 @@ if (!["production", "test"].includes(process.env.NODE_ENV || "")) {
app.use( app.use(
koaLogger((str) => { koaLogger((str) => {
serverLogger.info(str); serverLogger.info(str);
}), })
); );
// Delay // Delay
@ -48,7 +47,7 @@ if (!["production", "test"].includes(process.env.NODE_ENV || "")) {
app.use( app.use(
slow({ slow({
delay: 3000, delay: 3000,
}), })
); );
} }
} }
@ -71,7 +70,6 @@ const router = new Router();
// Routing // Routing
router.use(activityPub.routes()); router.use(activityPub.routes());
router.use(nodeinfo.routes());
router.use(wellKnown.routes()); router.use(wellKnown.routes());
router.get("/avatar/@:acct", async (ctx) => { router.get("/avatar/@:acct", async (ctx) => {
@ -113,7 +111,7 @@ router.get("/verify-email/:code", async (ctx) => {
{ {
emailVerified: true, emailVerified: true,
emailVerifyCode: null, emailVerifyCode: null,
}, }
); );
publishMainStream( publishMainStream(
@ -125,8 +123,8 @@ router.get("/verify-email/:code", async (ctx) => {
{ {
detail: true, detail: true,
includeSecrets: true, includeSecrets: true,
}, }
), )
); );
} else { } else {
ctx.status = 404; ctx.status = 404;
@ -163,12 +161,12 @@ export default () =>
switch ((e as any).code) { switch ((e as any).code) {
case "EACCES": case "EACCES":
serverLogger.error( serverLogger.error(
`You do not have permission to listen on port ${config.port}.`, `You do not have permission to listen on port ${config.port}.`
); );
break; break;
case "EADDRINUSE": case "EADDRINUSE":
serverLogger.error( serverLogger.error(
`Port ${config.port} is already in use by another process.`, `Port ${config.port} is already in use by another process.`
); );
break; break;
default: default:

View File

@ -1,119 +0,0 @@
import Router from "@koa/router";
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { Users, Notes } from "@/models/index.js";
import { IsNull, MoreThan } from "typeorm";
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
import { Cache } from "@/misc/cache.js";
const router = new Router();
const nodeinfo2_1path = "/nodeinfo/2.1";
const nodeinfo2_0path = "/nodeinfo/2.0";
// to cleo: leave this http or bonks
export const links = [
{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
href: config.url + nodeinfo2_1path,
},
{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
href: config.url + nodeinfo2_0path,
},
];
const nodeinfo2 = async () => {
const now = Date.now();
const [meta, total, activeHalfyear, activeMonth, localPosts] =
await Promise.all([
fetchMeta(true),
Users.count({ where: { host: IsNull() } }),
Users.count({
where: {
host: IsNull(),
lastActiveDate: MoreThan(new Date(now - 15552000000)),
},
}),
Users.count({
where: {
host: IsNull(),
lastActiveDate: MoreThan(new Date(now - 2592000000)),
},
}),
Notes.count({ where: { userHost: IsNull() } }),
]);
const proxyAccount = meta.proxyAccountId
? await Users.pack(meta.proxyAccountId).catch(() => null)
: null;
return {
software: {
name: "calckey",
version: config.version,
repository: meta.repositoryUrl,
homepage: "https://calckey.cloud",
},
protocols: ["activitypub"],
services: {
inbound: [] as string[],
outbound: ["atom1.0", "rss2.0"],
},
openRegistrations: !meta.disableRegistration,
usage: {
users: { total, activeHalfyear, activeMonth },
localPosts,
localComments: 0,
},
metadata: {
nodeName: meta.name,
nodeDescription: meta.description,
maintainer: {
name: meta.maintainerName,
email: meta.maintainerEmail,
},
langs: meta.langs,
tosUrl: meta.ToSUrl,
repositoryUrl: meta.repositoryUrl,
feedbackUrl: meta.feedbackUrl,
disableRegistration: meta.disableRegistration,
disableLocalTimeline: meta.disableLocalTimeline,
disableRecommendedTimeline: meta.disableRecommendedTimeline,
disableGlobalTimeline: meta.disableGlobalTimeline,
emailRequiredForSignup: meta.emailRequiredForSignup,
enableHcaptcha: meta.enableHcaptcha,
enableRecaptcha: meta.enableRecaptcha,
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH,
enableTwitterIntegration: meta.enableTwitterIntegration,
enableGithubIntegration: meta.enableGithubIntegration,
enableDiscordIntegration: meta.enableDiscordIntegration,
enableEmail: meta.enableEmail,
enableServiceWorker: meta.enableServiceWorker,
proxyAccountName: proxyAccount ? proxyAccount.username : null,
themeColor: meta.themeColor || "#31748f",
},
};
};
const cache = new Cache<Awaited<ReturnType<typeof nodeinfo2>>>(1000 * 60 * 10);
router.get(nodeinfo2_1path, async (ctx) => {
const base = await cache.fetch(null, () => nodeinfo2());
ctx.body = { version: "2.1", ...base };
ctx.set("Cache-Control", "public, max-age=600");
});
router.get(nodeinfo2_0path, async (ctx) => {
const base = await cache.fetch(null, () => nodeinfo2());
// @ts-ignore
base.software.repository = undefined;
ctx.body = { version: "2.0", ...base };
ctx.set("Cache-Control", "public, max-age=600");
});
export default router;

View File

@ -1,13 +1,7 @@
import Router from "@koa/router"; import Router from "@koa/router";
import config from "@/config/index.js"; import config from "@/config/index.js";
import * as Acct from "@/misc/acct.js"; import {escapeAttribute, escapeValue} from "@/prelude/xml.js";
import { links } from "./nodeinfo.js";
import { escapeAttribute, escapeValue } from "@/prelude/xml.js";
import { Users } from "@/models/index.js";
import type { User } from "@/models/entities/user.js";
import type { FindOptionsWhere } from "typeorm";
import { IsNull } from "typeorm";
// Init router // Init router
const router = new Router(); const router = new Router();
@ -23,10 +17,15 @@ const XRD = (
.map( .map(
({ element, value, attributes }) => ({ element, value, attributes }) =>
`<${Object.entries( `<${Object.entries(
(typeof attributes === "object" && attributes) || {}, (typeof attributes === "object" && attributes) || {}
).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element)}${ ).reduce(
typeof value === "string" ? `>${escapeValue(value)}</${element}` : "/" (a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`,
}>`, element
)}${
typeof value === "string"
? `>${escapeValue(value)}</${element}`
: "/"
}>`
) )
.reduce((a, c) => a + c, "")}</XRD>`; .reduce((a, c) => a + c, "")}</XRD>`;
@ -90,99 +89,11 @@ if (config.twa != null) {
}); });
} }
router.get("/.well-known/nodeinfo", async (ctx) => {
ctx.body = { links };
});
/* TODO /* TODO
router.get('/.well-known/change-password', async ctx => { router.get('/.well-known/change-password', async ctx => {
}); });
*/ */
router.get(webFingerPath, async (ctx) => {
const fromId = (id: User["id"]): FindOptionsWhere<User> => ({
id,
host: IsNull(),
isSuspended: false,
});
const generateQuery = (resource: string): FindOptionsWhere<User> | number =>
resource.startsWith(`${config.url.toLowerCase()}/users/`)
? fromId(resource.split("/").pop()!)
: fromAcct(
Acct.parse(
resource.startsWith(`${config.url.toLowerCase()}/@`)
? resource.split("/").pop()!
: resource.startsWith("acct:")
? resource.slice("acct:".length)
: resource,
),
);
const fromAcct = (acct: Acct.Acct): FindOptionsWhere<User> | number =>
!acct.host || acct.host === config.host.toLowerCase()
? {
usernameLower: acct.username,
host: IsNull(),
isSuspended: false,
}
: 422;
if (typeof ctx.query.resource !== "string") {
ctx.status = 400;
return;
}
const query = generateQuery(ctx.query.resource.toLowerCase());
if (typeof query === "number") {
ctx.status = query;
return;
}
const user = await Users.findOneBy(query);
if (user == null) {
ctx.status = 404;
return;
}
const subject = `acct:${user.username}@${config.host}`;
const self = {
rel: "self",
type: "application/activity+json",
href: `${config.url}/users/${user.id}`,
};
const profilePage = {
rel: "http://webfinger.net/rel/profile-page",
type: "text/html",
href: `${config.url}/@${user.username}`,
};
const subscribe = {
rel: "http://ostatus.org/schema/1.0/subscribe",
template: `${config.url}/authorize-follow?acct={uri}`,
};
if (ctx.accepts(jrd, xrd) === xrd) {
ctx.body = XRD(
{ element: "Subject", value: subject },
{ element: "Link", attributes: self },
{ element: "Link", attributes: profilePage },
{ element: "Link", attributes: subscribe },
);
ctx.type = xrd;
} else {
ctx.body = {
subject,
links: [self, profilePage, subscribe],
};
ctx.type = jrd;
}
ctx.vary("Accept");
ctx.set("Cache-Control", "public, max-age=180");
});
// Return 404 for other .well-known // Return 404 for other .well-known
router.all(allPath, async (ctx) => { router.all(allPath, async (ctx) => {
ctx.status = 404; ctx.status = 404;