@rmdes/indiekit-endpoint-activitypub 1.0.28 → 1.0.29
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.
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* POST: saves updated profile fields back to ap_profile
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const ACTOR_TYPES = ["Person", "Service", "Organization"];
|
|
9
|
+
|
|
8
10
|
export function profileGetController(mountPath) {
|
|
9
11
|
return async (request, response, next) => {
|
|
10
12
|
try {
|
|
@@ -18,6 +20,7 @@ export function profileGetController(mountPath) {
|
|
|
18
20
|
title: response.locals.__("activitypub.profile.title"),
|
|
19
21
|
mountPath,
|
|
20
22
|
profile,
|
|
23
|
+
actorTypes: ACTOR_TYPES,
|
|
21
24
|
result: null,
|
|
22
25
|
});
|
|
23
26
|
} catch (error) {
|
|
@@ -42,10 +45,23 @@ export function profilePostController(mountPath) {
|
|
|
42
45
|
url,
|
|
43
46
|
icon,
|
|
44
47
|
image,
|
|
48
|
+
actorType,
|
|
45
49
|
manuallyApprovesFollowers,
|
|
46
50
|
authorizedFetch,
|
|
47
51
|
} = request.body;
|
|
48
52
|
|
|
53
|
+
// Parse profile links (attachments) from form arrays
|
|
54
|
+
const linkNames = [].concat(request.body["link_name[]"] || []);
|
|
55
|
+
const linkValues = [].concat(request.body["link_value[]"] || []);
|
|
56
|
+
const attachments = [];
|
|
57
|
+
for (let i = 0; i < linkNames.length; i++) {
|
|
58
|
+
const n = linkNames[i]?.trim();
|
|
59
|
+
const v = linkValues[i]?.trim();
|
|
60
|
+
if (n && v) {
|
|
61
|
+
attachments.push({ name: n, value: v });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
const update = {
|
|
50
66
|
$set: {
|
|
51
67
|
name: name?.trim() || "",
|
|
@@ -53,8 +69,10 @@ export function profilePostController(mountPath) {
|
|
|
53
69
|
url: url?.trim() || "",
|
|
54
70
|
icon: icon?.trim() || "",
|
|
55
71
|
image: image?.trim() || "",
|
|
72
|
+
actorType: ACTOR_TYPES.includes(actorType) ? actorType : "Person",
|
|
56
73
|
manuallyApprovesFollowers: manuallyApprovesFollowers === "true",
|
|
57
74
|
authorizedFetch: authorizedFetch === "true",
|
|
75
|
+
attachments,
|
|
58
76
|
updatedAt: new Date().toISOString(),
|
|
59
77
|
},
|
|
60
78
|
};
|
|
@@ -67,6 +85,7 @@ export function profilePostController(mountPath) {
|
|
|
67
85
|
title: response.locals.__("activitypub.profile.title"),
|
|
68
86
|
mountPath,
|
|
69
87
|
profile,
|
|
88
|
+
actorTypes: ACTOR_TYPES,
|
|
70
89
|
result: {
|
|
71
90
|
type: "success",
|
|
72
91
|
text: response.locals.__("activitypub.profile.saved"),
|
package/lib/federation-setup.js
CHANGED
|
@@ -197,7 +197,11 @@ export function setupFederation(options) {
|
|
|
197
197
|
personOptions.published = Temporal.Instant.from(profile.createdAt);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
// Actor type from profile overrides config default
|
|
201
|
+
const profileActorType = profile.actorType || actorType;
|
|
202
|
+
const ResolvedActorClass = actorTypeMap[profileActorType] || ActorClass;
|
|
203
|
+
|
|
204
|
+
return new ResolvedActorClass(personOptions);
|
|
201
205
|
},
|
|
202
206
|
)
|
|
203
207
|
.mapHandle((_ctx, username) => {
|
package/locales/en.json
CHANGED
|
@@ -38,6 +38,14 @@
|
|
|
38
38
|
"imageHint": "URL to a banner image shown at the top of your profile",
|
|
39
39
|
"manualApprovalLabel": "Manually approve followers",
|
|
40
40
|
"manualApprovalHint": "When enabled, follow requests require your approval before they take effect",
|
|
41
|
+
"actorTypeLabel": "Actor type",
|
|
42
|
+
"actorTypeHint": "How your account appears in the fediverse. Person for individuals, Service for bots or automated accounts, Organization for groups or companies.",
|
|
43
|
+
"linksLabel": "Profile links",
|
|
44
|
+
"linksHint": "Links shown on your fediverse profile. Add your website, social accounts, or other URLs. Pages that link back with rel=\"me\" will show as verified on Mastodon.",
|
|
45
|
+
"linkNameLabel": "Label",
|
|
46
|
+
"linkValueLabel": "URL",
|
|
47
|
+
"addLink": "Add link",
|
|
48
|
+
"removeLink": "Remove",
|
|
41
49
|
"authorizedFetchLabel": "Require authorized fetch (secure mode)",
|
|
42
50
|
"authorizedFetchHint": "When enabled, only servers with valid HTTP Signatures can fetch your actor and collections. This improves privacy but may reduce compatibility with some clients.",
|
|
43
51
|
"save": "Save profile",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
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",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
{% from "input/macro.njk" import input with context %}
|
|
5
5
|
{% from "textarea/macro.njk" import textarea with context %}
|
|
6
6
|
{% from "checkboxes/macro.njk" import checkboxes with context %}
|
|
7
|
+
{% from "radios/macro.njk" import radios with context %}
|
|
7
8
|
{% from "button/macro.njk" import button with context %}
|
|
8
9
|
{% from "notification-banner/macro.njk" import notificationBanner with context %}
|
|
9
10
|
{% from "prose/macro.njk" import prose with context %}
|
|
@@ -57,6 +58,50 @@
|
|
|
57
58
|
type: "url"
|
|
58
59
|
}) }}
|
|
59
60
|
|
|
61
|
+
{{ radios({
|
|
62
|
+
name: "actorType",
|
|
63
|
+
fieldset: {
|
|
64
|
+
legend: __("activitypub.profile.actorTypeLabel")
|
|
65
|
+
},
|
|
66
|
+
hint: __("activitypub.profile.actorTypeHint"),
|
|
67
|
+
items: [{
|
|
68
|
+
label: "Person",
|
|
69
|
+
value: "Person"
|
|
70
|
+
}, {
|
|
71
|
+
label: "Service",
|
|
72
|
+
value: "Service"
|
|
73
|
+
}, {
|
|
74
|
+
label: "Organization",
|
|
75
|
+
value: "Organization"
|
|
76
|
+
}],
|
|
77
|
+
values: [profile.actorType or "Person"]
|
|
78
|
+
}) }}
|
|
79
|
+
|
|
80
|
+
<fieldset class="fieldset" style="margin-block-end: var(--space-l);">
|
|
81
|
+
<legend class="label">{{ __("activitypub.profile.linksLabel") }}</legend>
|
|
82
|
+
<p class="hint">{{ __("activitypub.profile.linksHint") }}</p>
|
|
83
|
+
|
|
84
|
+
<div id="profile-links">
|
|
85
|
+
{% if profile.attachments and profile.attachments.length > 0 %}
|
|
86
|
+
{% for att in profile.attachments %}
|
|
87
|
+
<div class="profile-link-row" style="display: grid; grid-template-columns: 1fr 2fr auto; gap: var(--space-s); align-items: end; margin-block-end: var(--space-s);">
|
|
88
|
+
<div>
|
|
89
|
+
<label class="label" for="link_name_{{ loop.index }}">{{ __("activitypub.profile.linkNameLabel") }}</label>
|
|
90
|
+
<input class="input" type="text" id="link_name_{{ loop.index }}" name="link_name[]" value="{{ att.name }}" placeholder="Website">
|
|
91
|
+
</div>
|
|
92
|
+
<div>
|
|
93
|
+
<label class="label" for="link_value_{{ loop.index }}">{{ __("activitypub.profile.linkValueLabel") }}</label>
|
|
94
|
+
<input class="input" type="url" id="link_value_{{ loop.index }}" name="link_value[]" value="{{ att.value }}" placeholder="https://example.com">
|
|
95
|
+
</div>
|
|
96
|
+
<button type="button" class="button button--small" onclick="this.closest('.profile-link-row').remove()" style="margin-block-end: 4px;">{{ __("activitypub.profile.removeLink") }}</button>
|
|
97
|
+
</div>
|
|
98
|
+
{% endfor %}
|
|
99
|
+
{% endif %}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<button type="button" class="button button--small" id="add-link-btn">{{ __("activitypub.profile.addLink") }}</button>
|
|
103
|
+
</fieldset>
|
|
104
|
+
|
|
60
105
|
{{ checkboxes({
|
|
61
106
|
name: "manuallyApprovesFollowers",
|
|
62
107
|
items: [
|
|
@@ -83,4 +128,57 @@
|
|
|
83
128
|
|
|
84
129
|
{{ button({ text: __("activitypub.profile.save") }) }}
|
|
85
130
|
</form>
|
|
131
|
+
|
|
132
|
+
<script>
|
|
133
|
+
(function() {
|
|
134
|
+
var linkCount = {{ (profile.attachments.length if profile.attachments) or 0 }};
|
|
135
|
+
document.getElementById('add-link-btn').addEventListener('click', function() {
|
|
136
|
+
linkCount++;
|
|
137
|
+
var container = document.getElementById('profile-links');
|
|
138
|
+
var row = document.createElement('div');
|
|
139
|
+
row.className = 'profile-link-row';
|
|
140
|
+
row.style.cssText = 'display: grid; grid-template-columns: 1fr 2fr auto; gap: var(--space-s); align-items: end; margin-block-end: var(--space-s);';
|
|
141
|
+
|
|
142
|
+
var nameDiv = document.createElement('div');
|
|
143
|
+
var nameLabel = document.createElement('label');
|
|
144
|
+
nameLabel.className = 'label';
|
|
145
|
+
nameLabel.setAttribute('for', 'link_name_' + linkCount);
|
|
146
|
+
nameLabel.textContent = 'Label';
|
|
147
|
+
var nameInput = document.createElement('input');
|
|
148
|
+
nameInput.className = 'input';
|
|
149
|
+
nameInput.type = 'text';
|
|
150
|
+
nameInput.id = 'link_name_' + linkCount;
|
|
151
|
+
nameInput.name = 'link_name[]';
|
|
152
|
+
nameInput.placeholder = 'Website';
|
|
153
|
+
nameDiv.appendChild(nameLabel);
|
|
154
|
+
nameDiv.appendChild(nameInput);
|
|
155
|
+
|
|
156
|
+
var valueDiv = document.createElement('div');
|
|
157
|
+
var valueLabel = document.createElement('label');
|
|
158
|
+
valueLabel.className = 'label';
|
|
159
|
+
valueLabel.setAttribute('for', 'link_value_' + linkCount);
|
|
160
|
+
valueLabel.textContent = 'URL';
|
|
161
|
+
var valueInput = document.createElement('input');
|
|
162
|
+
valueInput.className = 'input';
|
|
163
|
+
valueInput.type = 'url';
|
|
164
|
+
valueInput.id = 'link_value_' + linkCount;
|
|
165
|
+
valueInput.name = 'link_value[]';
|
|
166
|
+
valueInput.placeholder = 'https://example.com';
|
|
167
|
+
valueDiv.appendChild(valueLabel);
|
|
168
|
+
valueDiv.appendChild(valueInput);
|
|
169
|
+
|
|
170
|
+
var removeBtn = document.createElement('button');
|
|
171
|
+
removeBtn.type = 'button';
|
|
172
|
+
removeBtn.className = 'button button--small';
|
|
173
|
+
removeBtn.style.cssText = 'margin-block-end: 4px;';
|
|
174
|
+
removeBtn.textContent = 'Remove';
|
|
175
|
+
removeBtn.addEventListener('click', function() { row.remove(); });
|
|
176
|
+
|
|
177
|
+
row.appendChild(nameDiv);
|
|
178
|
+
row.appendChild(valueDiv);
|
|
179
|
+
row.appendChild(removeBtn);
|
|
180
|
+
container.appendChild(row);
|
|
181
|
+
});
|
|
182
|
+
})();
|
|
183
|
+
</script>
|
|
86
184
|
{% endblock %}
|