aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json14
-rw-r--r--package.json1
-rw-r--r--vnext/server/db/Users.js14
-rw-r--r--vnext/server/index.js5
-rw-r--r--vnext/server/middleware/webfinger.js52
5 files changed, 85 insertions, 1 deletions
diff --git a/package-lock.json b/package-lock.json
index 69a8222e..7447cba6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"name": "juick",
"license": "AGPL-3.0-or-later",
"dependencies": {
+ "address-rfc2822": "^2.2.2",
"axios": "^1.7.7",
"axios-cache-interceptor": "^1.6.0",
"body-parser": "^1.20.3",
@@ -5805,6 +5806,14 @@
"node": ">=0.4.0"
}
},
+ "node_modules/address-rfc2822": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/address-rfc2822/-/address-rfc2822-2.2.2.tgz",
+ "integrity": "sha512-Kkl42jmfpSjkAtuGYnfD4cwGpGtNvrYaCdDFesb8z9GxlWNAaB/SLkDfmGxhh/qH/GfvKmMxt6Nt6Ek4qjFC7w==",
+ "dependencies": {
+ "email-addresses": "^5.0.0"
+ }
+ },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -8500,6 +8509,11 @@
"node": ">=0.12.0"
}
},
+ "node_modules/email-addresses": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
+ "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw=="
+ },
"node_modules/emittery": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
diff --git a/package.json b/package.json
index c397d0b3..b31416ab 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
+ "address-rfc2822": "^2.2.2",
"axios": "^1.7.7",
"axios-cache-interceptor": "^1.6.0",
"body-parser": "^1.20.3",
diff --git a/vnext/server/db/Users.js b/vnext/server/db/Users.js
index 3db3eb5b..a6148497 100644
--- a/vnext/server/db/Users.js
+++ b/vnext/server/db/Users.js
@@ -1,7 +1,7 @@
import { DataTypes, Op } from 'sequelize'
import db from './index'
-export const User = db.define('user', {
+const User = db.define('user', {
id: {
type: DataTypes.INTEGER,
primaryKey: true
@@ -9,6 +9,8 @@ export const User = db.define('user', {
nick: DataTypes.STRING,
banned: DataTypes.INTEGER,
last_seen: DataTypes.DATE
+}, {
+ timestamps: false
})
export const getMonthlyActiveUsers = async () => {
@@ -25,3 +27,13 @@ export const getMonthlyActiveUsers = async () => {
}
})
}
+
+export const getUserByName = async (name = '') => {
+ return User.findOne({
+ where: {
+ nick: {
+ [Op.eq]: name
+ }
+ }
+ })
+}
diff --git a/vnext/server/index.js b/vnext/server/index.js
index f27f0908..75b92270 100644
--- a/vnext/server/index.js
+++ b/vnext/server/index.js
@@ -15,6 +15,7 @@ import { instance } from './middleware/mastodon'
const PORT = process.env.LISTEN_PORT || 8081
import path from 'path'
import { webhook, webhookPath } from './durov'
+import { webfinger } from './middleware/webfinger'
// initialize the application and create the routes
const app = express()
@@ -27,6 +28,10 @@ router.get('/api/v2/oembed', oembed)
router.get('/api/v2/urlexpand', urlExpand)
router.get('/api/apps/android/releases', releases)
+// WebFinger
+
+router.get('/.well-known/webfinger', webfinger)
+
// Mastodon API
router.get('/api/v2/instance', instance)
diff --git a/vnext/server/middleware/webfinger.js b/vnext/server/middleware/webfinger.js
new file mode 100644
index 00000000..9800fc01
--- /dev/null
+++ b/vnext/server/middleware/webfinger.js
@@ -0,0 +1,52 @@
+import config from 'config'
+import addrparser from 'address-rfc2822'
+
+import { getUserByName } from '../db/Users'
+
+const baseUrl = config.get('service.baseURL')
+
+/**
+ * WebFinger endpoint
+ * @type {import('express').RequestParamHandler}
+ */
+export const webfinger = async (req, res) => {
+ const resource = req.query.resource
+ if (resource) {
+ const acct = resource.substring(5) // drop "acct:"
+ const addresses = parseAddress(acct)
+ if (addresses && addresses.length == 1) {
+ const address = addresses[0]
+ const ourAddress = new URL(baseUrl)
+ if (address.host() === ourAddress.hostname) {
+ const user = await getUserByName(addresses[0].user())
+ if (user) {
+ return res.json({
+ subject: resource,
+ links: [
+ {
+ rel: 'self',
+ type: 'application/activity+json',
+ href: `${baseUrl}/u/${user.nick}`
+ }
+ ]
+ })
+ } else {
+ return res.status(404).send('User not found')
+ }
+ } else {
+ return res.status(404).send('Address not found')
+ }
+ } else {
+ return res.status(400).send('Invalid resource')
+ }
+ }
+ res.status(400).send('Missing `resource` param')
+}
+
+const parseAddress = (address = '') => {
+ try {
+ return addrparser.parse(address, { startAt: 'address' })
+ } catch {
+ return undefined
+ }
+}