@rmdes/indiekit-endpoint-activitypub 3.11.2 → 3.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/mastodon/router.js
CHANGED
|
@@ -67,18 +67,10 @@ export function createMastodonRouter({ collections, pluginOptions = {} }) {
|
|
|
67
67
|
|
|
68
68
|
// ─── Body parsers ───────────────────────────────────────────────────────
|
|
69
69
|
// Mastodon clients send JSON, form-urlencoded, and occasionally text/plain.
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
router.use("/api", (req, res, next) => {
|
|
75
|
-
if (req.is("multipart/form-data")) return next();
|
|
76
|
-
jsonParser(req, res, next);
|
|
77
|
-
});
|
|
78
|
-
router.use("/api", (req, res, next) => {
|
|
79
|
-
if (req.is("multipart/form-data")) return next();
|
|
80
|
-
urlencodedParser(req, res, next);
|
|
81
|
-
});
|
|
70
|
+
// Note: multipart/form-data is handled globally by express-fileupload
|
|
71
|
+
// (configured in Indiekit's express.js), so no multer needed here.
|
|
72
|
+
router.use("/api", express.json());
|
|
73
|
+
router.use("/api", express.urlencoded({ extended: true }));
|
|
82
74
|
router.use("/oauth", express.json());
|
|
83
75
|
router.use("/oauth", express.urlencoded({ extended: true }));
|
|
84
76
|
|
|
@@ -5,18 +5,16 @@
|
|
|
5
5
|
* POST /api/v1/media — legacy upload (same as v2)
|
|
6
6
|
* GET /api/v1/media/:id — get media attachment metadata
|
|
7
7
|
* PUT /api/v1/media/:id — update media metadata (description/focus)
|
|
8
|
+
*
|
|
9
|
+
* File uploads are handled by express-fileupload (configured globally by
|
|
10
|
+
* Indiekit's express.js). Files arrive on req.files, NOT req.file (multer).
|
|
8
11
|
*/
|
|
9
12
|
import express from "express";
|
|
10
|
-
import multer from "multer";
|
|
11
13
|
import { ObjectId } from "mongodb";
|
|
12
14
|
import { tokenRequired } from "../middleware/token-required.js";
|
|
13
15
|
import { scopeRequired } from "../middleware/scope-required.js";
|
|
14
16
|
|
|
15
17
|
const router = express.Router(); // eslint-disable-line new-cap
|
|
16
|
-
const upload = multer({
|
|
17
|
-
storage: multer.memoryStorage(),
|
|
18
|
-
limits: { fileSize: 40 * 1024 * 1024 },
|
|
19
|
-
});
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
20
|
* Determine Mastodon media type from MIME type.
|
|
@@ -54,6 +52,7 @@ function serializeMediaAttachment(doc) {
|
|
|
54
52
|
|
|
55
53
|
/**
|
|
56
54
|
* Upload file to the Micropub media endpoint.
|
|
55
|
+
* Accepts an express-fileupload file object (has .data Buffer, .mimetype, .name).
|
|
57
56
|
* Returns the URL from the Location header.
|
|
58
57
|
*/
|
|
59
58
|
async function uploadToMediaEndpoint(file, application, token) {
|
|
@@ -67,8 +66,8 @@ async function uploadToMediaEndpoint(file, application, token) {
|
|
|
67
66
|
: new URL(mediaEndpoint, application.url).href;
|
|
68
67
|
|
|
69
68
|
const formData = new FormData();
|
|
70
|
-
const blob = new Blob([file.
|
|
71
|
-
formData.append("file", blob, file.
|
|
69
|
+
const blob = new Blob([file.data], { type: file.mimetype });
|
|
70
|
+
formData.append("file", blob, file.name);
|
|
72
71
|
|
|
73
72
|
const response = await fetch(mediaUrl, {
|
|
74
73
|
method: "POST",
|
|
@@ -95,15 +94,20 @@ router.post(
|
|
|
95
94
|
"/api/v2/media",
|
|
96
95
|
tokenRequired,
|
|
97
96
|
scopeRequired("write", "write:media"),
|
|
98
|
-
upload.single("file"),
|
|
99
97
|
async (req, res, next) => {
|
|
100
98
|
try {
|
|
101
99
|
const { application } = req.app.locals;
|
|
102
100
|
const collections = req.app.locals.mastodonCollections;
|
|
101
|
+
// Use IndieAuth token stored during OAuth authorization, falling back
|
|
102
|
+
// to session token (native reader) or Mastodon token (won't work for
|
|
103
|
+
// Micropub media endpoint but covers direct internal calls).
|
|
103
104
|
const token =
|
|
104
|
-
req.session?.access_token ||
|
|
105
|
+
req.session?.access_token ||
|
|
106
|
+
req.mastodonToken?.indieauthToken ||
|
|
107
|
+
req.mastodonToken?.accessToken;
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
const file = req.files?.file;
|
|
110
|
+
if (!file) {
|
|
107
111
|
return res.status(422).json({ error: "No file provided" });
|
|
108
112
|
}
|
|
109
113
|
|
|
@@ -113,17 +117,13 @@ router.post(
|
|
|
113
117
|
.json({ error: "Authentication required for media upload" });
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
const fileUrl = await uploadToMediaEndpoint(
|
|
117
|
-
req.file,
|
|
118
|
-
application,
|
|
119
|
-
token,
|
|
120
|
-
);
|
|
120
|
+
const fileUrl = await uploadToMediaEndpoint(file, application, token);
|
|
121
121
|
|
|
122
122
|
const doc = {
|
|
123
123
|
url: fileUrl,
|
|
124
124
|
description: req.body.description || "",
|
|
125
125
|
focus: req.body.focus || null,
|
|
126
|
-
mimeType:
|
|
126
|
+
mimeType: file.mimetype,
|
|
127
127
|
createdAt: new Date(),
|
|
128
128
|
};
|
|
129
129
|
|
|
@@ -143,15 +143,20 @@ router.post(
|
|
|
143
143
|
"/api/v1/media",
|
|
144
144
|
tokenRequired,
|
|
145
145
|
scopeRequired("write", "write:media"),
|
|
146
|
-
upload.single("file"),
|
|
147
146
|
async (req, res, next) => {
|
|
148
147
|
try {
|
|
149
148
|
const { application } = req.app.locals;
|
|
150
149
|
const collections = req.app.locals.mastodonCollections;
|
|
150
|
+
// Use IndieAuth token stored during OAuth authorization, falling back
|
|
151
|
+
// to session token (native reader) or Mastodon token (won't work for
|
|
152
|
+
// Micropub media endpoint but covers direct internal calls).
|
|
151
153
|
const token =
|
|
152
|
-
req.session?.access_token ||
|
|
154
|
+
req.session?.access_token ||
|
|
155
|
+
req.mastodonToken?.indieauthToken ||
|
|
156
|
+
req.mastodonToken?.accessToken;
|
|
153
157
|
|
|
154
|
-
|
|
158
|
+
const file = req.files?.file;
|
|
159
|
+
if (!file) {
|
|
155
160
|
return res.status(422).json({ error: "No file provided" });
|
|
156
161
|
}
|
|
157
162
|
|
|
@@ -161,17 +166,13 @@ router.post(
|
|
|
161
166
|
.json({ error: "Authentication required for media upload" });
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
const fileUrl = await uploadToMediaEndpoint(
|
|
165
|
-
req.file,
|
|
166
|
-
application,
|
|
167
|
-
token,
|
|
168
|
-
);
|
|
169
|
+
const fileUrl = await uploadToMediaEndpoint(file, application, token);
|
|
169
170
|
|
|
170
171
|
const doc = {
|
|
171
172
|
url: fileUrl,
|
|
172
173
|
description: req.body.description || "",
|
|
173
174
|
focus: req.body.focus || null,
|
|
174
|
-
mimeType:
|
|
175
|
+
mimeType: file.mimetype,
|
|
175
176
|
createdAt: new Date(),
|
|
176
177
|
};
|
|
177
178
|
|
|
@@ -388,6 +388,9 @@ router.post("/oauth/authorize", async (req, res, next) => {
|
|
|
388
388
|
redirectUri: redirect_uri,
|
|
389
389
|
codeChallenge: code_challenge || null,
|
|
390
390
|
codeChallengeMethod: code_challenge_method || null,
|
|
391
|
+
// Store the IndieAuth session token so Mastodon API routes can call
|
|
392
|
+
// Micropub/media endpoints on behalf of this user (single-user server).
|
|
393
|
+
indieauthToken: req.session?.access_token || null,
|
|
391
394
|
createdAt: new Date(),
|
|
392
395
|
expiresAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes
|
|
393
396
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"indiekit",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"express": "^5.0.0",
|
|
46
46
|
"express-rate-limit": "^7.5.1",
|
|
47
47
|
"ioredis": "^5.9.3",
|
|
48
|
-
"multer": "^2.1.1",
|
|
49
48
|
"sanitize-html": "^2.13.1",
|
|
50
49
|
"unfurl.js": "^6.4.0"
|
|
51
50
|
},
|