fedipod 1.43.0 → 1.44.0
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/README.md +16 -0
- package/lib/gateway/front-core.mjs +19 -9
- package/lib/gateway/front-pages.mjs +2 -4
- package/lib/gateway/held-mail.mjs +7 -0
- package/package.json +3 -3
- package/scripts/check-pod-calls.mjs +1 -1
- package/scripts/start.mjs +13 -0
- package/web/app/boot.mjs +65 -7
- package/web/app/dist/boot.js +764 -10
- package/web/app/dist/boot.js.map +4 -4
- package/web/app/forum-form.mjs +38 -0
- package/web/app/forum-support.mjs +11 -0
- package/web/app/index.html +29 -4
- package/web/app/signup-forum.mjs +52 -0
- package/web/app/signup.mjs +2 -2
- package/web/front/new-account.html +8 -16
- package/web/front/new-account.js +3 -7
- package/web/front/install.sh +0 -69
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// forum-form.mjs — what the sign-up page's forum fields say, read into what
|
|
2
|
+
// the forum is made from. No imports, so a test reads it as the page does.
|
|
3
|
+
|
|
4
|
+
export const SLUG = /^[a-z0-9][a-z0-9-]{0,62}$/u;
|
|
5
|
+
|
|
6
|
+
// One category per line: a short name for the address, then a colon and the
|
|
7
|
+
// name readers see; a line with no colon is both.
|
|
8
|
+
export function categoriesFrom(text) {
|
|
9
|
+
const lines = String(text || '').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
10
|
+
if (!lines.length) throw new Error('Categories: at least one is required.');
|
|
11
|
+
const out = [];
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
const i = line.indexOf(':');
|
|
15
|
+
const slug = (i < 0 ? line : line.slice(0, i)).trim().toLowerCase();
|
|
16
|
+
const name = (i < 0 ? '' : line.slice(i + 1)).trim() || slug;
|
|
17
|
+
if (!SLUG.test(slug)) throw new Error(`Categories: "${slug}" is not a short name. Letters, digits and hyphens, starting with a letter or digit.`);
|
|
18
|
+
if (seen.has(slug)) throw new Error(`Categories: "${slug}" is listed twice.`);
|
|
19
|
+
seen.add(slug);
|
|
20
|
+
out.push({ slug, name: name.slice(0, 200) });
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Who moderates, as an actor id: an address at this site, or a web address
|
|
26
|
+
// given outright. Empty is nobody named yet.
|
|
27
|
+
export function moderatorFrom(text, frontOrigin) {
|
|
28
|
+
const s = String(text || '').trim();
|
|
29
|
+
if (!s) return null;
|
|
30
|
+
if (/^https?:\/\//iu.test(s)) {
|
|
31
|
+
try { return new URL(s).href; } catch { throw new Error('Moderator: not a valid web address.'); }
|
|
32
|
+
}
|
|
33
|
+
const m = /^@?([a-z0-9][a-z0-9._-]*)@([a-z0-9.-]+(?::\d+)?)$/iu.exec(s);
|
|
34
|
+
if (!m) throw new Error('Moderator: write it as @name@server, or as a web address.');
|
|
35
|
+
const front = frontOrigin ? new URL(frontOrigin) : null;
|
|
36
|
+
if (front && m[2].toLowerCase() === front.host.toLowerCase()) return `${front.origin}/u/${m[1].toLowerCase()}/ap/actor`;
|
|
37
|
+
throw new Error(`Moderator: only an address at ${front ? front.host : 'this site'} can be named here. Others can be added on the forum's settings page.`);
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// forum-support.mjs — whether this site's sign-up page offers a forum. The
|
|
2
|
+
// page's own code never imports the forum; this file is the one place that
|
|
3
|
+
// names the forum's setup module, and the page hides the choice when it is
|
|
4
|
+
// null. The counterpart of netlify/forum-support.mjs, which decides whether
|
|
5
|
+
// the Gateway behind this page keeps forums: the two go together.
|
|
6
|
+
//
|
|
7
|
+
// with forums import * as setup from '../../packages/fedipod-bb/src/setup.mjs'; export default setup;
|
|
8
|
+
// without forums export default null;
|
|
9
|
+
|
|
10
|
+
import * as setup from '../../packages/fedipod-bb/src/setup.mjs';
|
|
11
|
+
export default setup;
|
package/web/app/index.html
CHANGED
|
@@ -41,13 +41,14 @@ code { background: rgba(120,120,120,.18); padding: .05em .35em; border-radius: 4
|
|
|
41
41
|
fieldset { border: 1px solid var(--line); border-radius: .4rem; margin: 1rem 0; padding: .75rem 1rem 1rem; }
|
|
42
42
|
legend { padding: 0 .3rem; font-weight: 600; }
|
|
43
43
|
label { display: block; margin: .75rem 0 .2rem; }
|
|
44
|
-
input[type=text], input[type=email], input[type=url], input[type=password], select {
|
|
44
|
+
input[type=text], input[type=email], input[type=url], input[type=password], select, textarea {
|
|
45
45
|
font: inherit; font-size: 1rem; width: 100%; padding: .55rem; box-sizing: border-box;
|
|
46
46
|
border: 1px solid var(--field-border); border-radius: .3rem;
|
|
47
47
|
background: var(--field-bg); color: var(--field-text); }
|
|
48
48
|
/* A list to pick from looks different from a box to type in: its own tint,
|
|
49
49
|
the accent border, and the same on its options. */
|
|
50
50
|
select { background: var(--select-bg); border-color: var(--select-border); }
|
|
51
|
+
textarea { min-height: 6.5rem; resize: vertical; }
|
|
51
52
|
select option { background: var(--select-bg); color: var(--field-text); }
|
|
52
53
|
::placeholder { color: var(--sub); opacity: 1; }
|
|
53
54
|
.choice { display: block; margin: .4rem 0; font-weight: normal; }
|
|
@@ -145,6 +146,14 @@ a { color: var(--accent); }
|
|
|
145
146
|
<fieldset>
|
|
146
147
|
<legend>Your Solid Pod</legend>
|
|
147
148
|
|
|
149
|
+
<div role="radiogroup" aria-labelledby="making-label" id="making-group">
|
|
150
|
+
<span id="making-label" class="grouplabel">What you are making</span>
|
|
151
|
+
<label class="choice"><input type="radio" name="making" value="account" checked> An account for yourself</label>
|
|
152
|
+
<label class="choice"><input type="radio" name="making" value="forum"> A forum</label>
|
|
153
|
+
<p class="hint" id="making-hint" hidden>A forum has a pod of its own. Make a new pod for it at the
|
|
154
|
+
provider, or sign in with a pod that holds nothing else. The forum's address will be at this site.</p>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
148
157
|
<div role="radiogroup" aria-labelledby="podtype-label">
|
|
149
158
|
<span id="podtype-label" class="grouplabel">Pod type</span>
|
|
150
159
|
<label class="choice"><input type="radio" name="mode" value="existing"> An existing pod</label>
|
|
@@ -186,7 +195,7 @@ a { color: var(--accent); }
|
|
|
186
195
|
<!-- Screen 2: Your Fediverse Identity — shown on return from the pod's login -->
|
|
187
196
|
<div class="step" id="step-2" hidden>
|
|
188
197
|
<fieldset>
|
|
189
|
-
<legend>Your Fediverse Identity</legend>
|
|
198
|
+
<legend id="identity-legend">Your Fediverse Identity</legend>
|
|
190
199
|
|
|
191
200
|
<span id="signed-pod-label" class="grouplabel">Your pod</span>
|
|
192
201
|
<p id="signed-pod" role="status" aria-labelledby="signed-pod-label"></p>
|
|
@@ -204,12 +213,27 @@ a { color: var(--accent); }
|
|
|
204
213
|
Your posts, followers and key come with you, and your followers are told the
|
|
205
214
|
new address. Keep the same handle, or choose a new one here.</p>
|
|
206
215
|
|
|
207
|
-
<label for="handle">Fediverse handle</label>
|
|
216
|
+
<label for="handle" id="handle-label">Fediverse handle</label>
|
|
208
217
|
<input type="text" id="handle" name="handle" autocomplete="off"
|
|
209
218
|
placeholder="the handle part of your address: @HANDLE@server" aria-describedby="handle-hint">
|
|
210
219
|
<p class="hint" id="handle-hint">The name in your Fediverse address. This is permanent.
|
|
211
220
|
Letters, digits, and hyphens only.</p>
|
|
212
221
|
|
|
222
|
+
<div id="forum-fields" hidden>
|
|
223
|
+
<label for="forumName">Forum name</label>
|
|
224
|
+
<input type="text" id="forumName" name="forumName" autocomplete="off" placeholder="The name readers see">
|
|
225
|
+
<label for="categories">Categories, one per line</label>
|
|
226
|
+
<textarea id="categories" name="categories" autocomplete="off" spellcheck="false" aria-describedby="categories-hint"
|
|
227
|
+
placeholder="general: General Discussion software: Software"></textarea>
|
|
228
|
+
<p class="hint" id="categories-hint">Each line is a short name for the category's address, then a colon and
|
|
229
|
+
the name readers see. Short names are letters, digits and hyphens, and are permanent.</p>
|
|
230
|
+
<label for="moderator">Your address at this site, to moderate the forum as</label>
|
|
231
|
+
<input type="text" id="moderator" name="moderator" autocomplete="off" spellcheck="false" aria-describedby="moderator-hint"
|
|
232
|
+
placeholder="@you@this-site">
|
|
233
|
+
<p class="hint" id="moderator-hint">Optional. Signing in with the forum's own pod moderates too. More
|
|
234
|
+
moderators can be named on the forum's settings page later.</p>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
213
237
|
<div role="radiogroup" aria-labelledby="shape-label" id="shape-group">
|
|
214
238
|
<span id="shape-label" class="grouplabel">Where your address lives</span>
|
|
215
239
|
<label class="choice"><input type="radio" name="shape" value="pod" checked> On your pod</label>
|
|
@@ -221,7 +245,7 @@ a { color: var(--accent); }
|
|
|
221
245
|
<span id="addr-label" class="grouplabel">Your Fediverse address</span>
|
|
222
246
|
<p class="address" id="preview" role="status" aria-live="polite" aria-labelledby="addr-label">@…@…</p>
|
|
223
247
|
</fieldset>
|
|
224
|
-
<p class="hint">The handle and the pod cannot be changed afterwards. Everything else — display
|
|
248
|
+
<p class="hint" id="after-hint">The handle and the pod cannot be changed afterwards. Everything else — display
|
|
225
249
|
name, bio, pictures — you set later in the client.</p>
|
|
226
250
|
<p class="err" id="form-error" role="alert"></p>
|
|
227
251
|
<div id="index-ask" role="alertdialog" aria-labelledby="index-ask-text" hidden>
|
|
@@ -246,6 +270,7 @@ a { color: var(--accent); }
|
|
|
246
270
|
<h2 id="running-title">Setting up…</h2>
|
|
247
271
|
<ul class="steps" id="steps" aria-live="polite"></ul>
|
|
248
272
|
<p class="err" id="run-error" role="alert"></p>
|
|
273
|
+
<p id="run-done" hidden><a id="run-done-link" href="#">Open your forum</a></p>
|
|
249
274
|
<p id="run-actions" hidden>
|
|
250
275
|
<button type="button" id="run-back">Start over</button>
|
|
251
276
|
<button type="button" class="primary" id="run-retry">Try again</button>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// signup-forum.mjs — a forum made from the sign-up page. The person made a pod
|
|
2
|
+
// for it at a provider and is signed in as that pod; this writes the forum on
|
|
3
|
+
// the pod, takes its addresses here, and names this site its keeper. The
|
|
4
|
+
// keeper's first run, started by the last switch, mints the forum's keys and
|
|
5
|
+
// publishes its actors. Nothing of the person's own account is touched, and
|
|
6
|
+
// nothing runs on their machine afterwards.
|
|
7
|
+
|
|
8
|
+
import forumSetup from './forum-support.mjs';
|
|
9
|
+
import { BrowserRemotePod } from './pod-remote.mjs';
|
|
10
|
+
import { HttpStorage } from '../../lib/core/storage.mjs';
|
|
11
|
+
import { podBaseOfWebId } from '../../lib/pod/urls.mjs';
|
|
12
|
+
import { resourceExists } from '../../lib/pod/root.mjs';
|
|
13
|
+
import { findAccount } from '../../lib/core/place.mjs';
|
|
14
|
+
import { handleProblem, assertFrontNameFree, readConfigAt } from './signup.mjs';
|
|
15
|
+
import { categoriesFrom, moderatorFrom } from './forum-form.mjs';
|
|
16
|
+
|
|
17
|
+
/** Whether this site makes forums at all (web/app/forum-support.mjs). */
|
|
18
|
+
export const forumsOffered = () => !!forumSetup;
|
|
19
|
+
|
|
20
|
+
export async function signUpForum({ handle, name, categories, moderator = '' }, { session, onStep = () => {}, frontOrigin = null } = {}) {
|
|
21
|
+
if (!forumSetup) throw new Error('This site does not host forums.');
|
|
22
|
+
if (!frontOrigin) throw new Error('A forum lives at a gateway, and this page has none.');
|
|
23
|
+
const bad = handleProblem(handle);
|
|
24
|
+
if (bad) throw new Error(`Forum handle: ${bad}`);
|
|
25
|
+
if (!String(name || '').trim()) throw new Error('A forum name is required.');
|
|
26
|
+
const cats = categoriesFrom(categories);
|
|
27
|
+
const mod = moderatorFrom(moderator, frontOrigin);
|
|
28
|
+
if (!session?.webId || typeof session.fetch !== 'function') throw new Error("sign in at the forum's pod first");
|
|
29
|
+
|
|
30
|
+
const webId = session.webId;
|
|
31
|
+
const pod = podBaseOfWebId(webId);
|
|
32
|
+
const remote = new BrowserRemotePod(session, { webId, role: 'signup', log: () => {} });
|
|
33
|
+
|
|
34
|
+
// A forum needs a pod of its own: not one that hosts an account, not one
|
|
35
|
+
// that holds a forum already; and its names must be free here.
|
|
36
|
+
onStep('pod', 'running');
|
|
37
|
+
if (await findAccount(remote, pod, readConfigAt(remote, pod))) {
|
|
38
|
+
throw new Error('This pod already hosts a FediPod account. A forum needs a pod of its own.');
|
|
39
|
+
}
|
|
40
|
+
if (await resourceExists(session.fetch, pod + forumSetup.ROOT)) {
|
|
41
|
+
throw new Error('This pod already holds a forum.');
|
|
42
|
+
}
|
|
43
|
+
for (const h of [handle, ...cats.map((c) => c.slug)]) await assertFrontNameFree(frontOrigin, h);
|
|
44
|
+
onStep('pod', 'ok');
|
|
45
|
+
|
|
46
|
+
const made = await forumSetup.setUpForumAtGateway({
|
|
47
|
+
remote, storageFor: (base, fetch) => new HttpStorage(base, fetch), fetch: (u, i) => session.fetch(u, i),
|
|
48
|
+
front: frontOrigin, ownerWebId: webId, remotePod: pod, handle, name: String(name).trim(), categories: cats,
|
|
49
|
+
moderators: mod ? [mod] : [], moderatorWebIds: [webId],
|
|
50
|
+
}, { onStep });
|
|
51
|
+
return { handle: made.handle, front: made.front, address: `@${made.handle}@${new URL(made.front).host}` };
|
|
52
|
+
}
|
package/web/app/signup.mjs
CHANGED
|
@@ -27,7 +27,7 @@ const keysDocFor = (pod, root) => `${pod}${root}ap-state/keys.json`;
|
|
|
27
27
|
const stateOf = (pod, root) => `${pod}${root}ap-state/`;
|
|
28
28
|
|
|
29
29
|
/** The account's config as it sits under `root`, read as its owner. */
|
|
30
|
-
const readConfigAt = (remote, pod) => (root) => podState.readConfig(remote, { state: stateOf(pod, root) });
|
|
30
|
+
export const readConfigAt = (remote, pod) => (root) => podState.readConfig(remote, { state: stateOf(pod, root) });
|
|
31
31
|
|
|
32
32
|
const HANDLE_RE = /^[a-z0-9-]{2,30}$/;
|
|
33
33
|
|
|
@@ -49,7 +49,7 @@ const PROGRESS = new Map();
|
|
|
49
49
|
const progressKey = (webId, a) => [webId, a.handle, a.shape || 'pod', a.container || ''].join('|');
|
|
50
50
|
|
|
51
51
|
/** A fronted name is one per gateway; a taken one is refused before anything is made. */
|
|
52
|
-
async function assertFrontNameFree(frontOrigin, handle) {
|
|
52
|
+
export async function assertFrontNameFree(frontOrigin, handle) {
|
|
53
53
|
const res = await fetch(`${frontOrigin.replace(/\/$/, '')}/api/handle?handle=${encodeURIComponent(handle)}`,
|
|
54
54
|
{ headers: { accept: 'application/json' } }).catch(() => null);
|
|
55
55
|
const d = res ? await res.json().catch(() => ({})) : null;
|
|
@@ -3,33 +3,25 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>FediPod
|
|
6
|
+
<title>FediPod Gateway</title>
|
|
7
7
|
<link rel="icon" href="/icons/gateway.svg" type="image/svg+xml">
|
|
8
8
|
<style>
|
|
9
|
-
:root { color-scheme: light dark; --
|
|
9
|
+
:root { color-scheme: light dark; --dim:#45443d; }
|
|
10
10
|
@media (prefers-color-scheme: dark) { :root { --dim:#c2c0b4; } }
|
|
11
11
|
body { font: 20px/1.6 Arial, Helvetica, sans-serif; max-width: 44rem; margin: 2rem auto; padding: 0 1.1rem; }
|
|
12
12
|
h1 { font-size: 1.7rem; margin: 0 0 .2rem; }
|
|
13
|
-
h2 { font-size: 1.2rem; margin: 1.4rem 0 .4rem; }
|
|
14
13
|
p.lede { color: var(--dim); margin-top: 0; }
|
|
15
14
|
.hint { color: var(--dim); font-size: .95rem; }
|
|
16
|
-
|
|
17
|
-
pre { background: rgba(120,120,120,.12); padding: .7rem .9rem; border-radius: 8px; overflow-x: auto; }
|
|
18
|
-
ol.steps li { margin: .5rem 0; }
|
|
15
|
+
a { text-decoration: underline; }
|
|
19
16
|
[hidden] { display: none !important; }
|
|
20
17
|
</style>
|
|
21
18
|
</head>
|
|
22
19
|
<body>
|
|
23
|
-
<h1>FediPod
|
|
24
|
-
<p class="lede">
|
|
25
|
-
|
|
26
|
-
<p class="hint" id="current-version" hidden>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<p>
|
|
30
|
-
FediPod is experimental and free to use. See the <a href="https://github.com/jeff-zucker/FediPod">FediPod repository</a> for details.
|
|
31
|
-
</p>
|
|
32
|
-
|
|
20
|
+
<h1>FediPod Gateway</h1>
|
|
21
|
+
<p class="lede">This is a FediPod Gateway: the door other Fediverse servers deliver to, for accounts whose data lives on Solid pods.</p>
|
|
22
|
+
<p>Accounts are made and used in the browser. <a href="/">Open FediPod</a> to create one or sign in. Nothing is installed.</p>
|
|
23
|
+
<p class="hint" id="current-version" hidden>This gateway runs FediPod <strong id="current-version-num"></strong>.</p>
|
|
24
|
+
<p>FediPod is experimental and free to use. See the <a href="https://github.com/jeff-zucker/FediPod">FediPod repository</a> for details.</p>
|
|
33
25
|
<script src="/new-account.js"></script>
|
|
34
26
|
</body>
|
|
35
27
|
</html>
|
package/web/front/new-account.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
// new-account.js — the
|
|
2
|
-
//
|
|
3
|
-
// It
|
|
4
|
-
// be served under `script-src 'self'`, which is what stops a stray bit of
|
|
5
|
-
// markup from becoming running code; inline script cannot be told apart from
|
|
6
|
-
// injected script by any policy. Served by the front at /new-account.js.
|
|
1
|
+
// new-account.js — the front's root page's script, in a file of its own so
|
|
2
|
+
// the page is served under `script-src 'self'`. Served by the front at
|
|
3
|
+
// /new-account.js. It fills in the version this gateway runs, nothing more.
|
|
7
4
|
fetch('/api/handle?handle=__probe__').then(r => r.json()).then(d => {
|
|
8
5
|
if (d.version) {
|
|
9
6
|
document.getElementById('current-version-num').textContent = d.version;
|
|
10
|
-
document.getElementById('current-version-cmd').textContent = `curl -fsSL ${location.origin}/install | sh`;
|
|
11
7
|
document.getElementById('current-version').hidden = false;
|
|
12
8
|
}
|
|
13
9
|
}).catch(() => {});
|
package/web/front/install.sh
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# FediPod installer. Usage: curl -fsSL https://fedipod.net/install | sh
|
|
3
|
-
# Installs into ~/FediPod (or $FEDIPOD_DIR); re-running updates the install.
|
|
4
|
-
#
|
|
5
|
-
# A signup page may append parameters (sh -s -- --gateway ... --secret ...);
|
|
6
|
-
# they are saved for the first start, whose setup opens pre-filled and
|
|
7
|
-
# attaches to the gateway when it completes.
|
|
8
|
-
set -eu
|
|
9
|
-
|
|
10
|
-
REPO="https://github.com/jeff-zucker/FediPod"
|
|
11
|
-
DIR="${FEDIPOD_DIR:-$HOME/FediPod}"
|
|
12
|
-
|
|
13
|
-
say() { printf '%s\n' "$*"; }
|
|
14
|
-
fail() { printf 'fedipod install: %s\n' "$*" >&2; exit 1; }
|
|
15
|
-
|
|
16
|
-
GATEWAY= SECRET= POD= ISSUER= HANDLE= KIND=person FRONTED=false
|
|
17
|
-
while [ $# -gt 0 ]; do
|
|
18
|
-
case "$1" in
|
|
19
|
-
--gateway) GATEWAY=$2; shift 2;;
|
|
20
|
-
--secret) SECRET=$2; shift 2;;
|
|
21
|
-
--pod) POD=$2; shift 2;;
|
|
22
|
-
--issuer) ISSUER=$2; shift 2;;
|
|
23
|
-
--handle) HANDLE=$2; shift 2;;
|
|
24
|
-
--kind) KIND=$2; shift 2;;
|
|
25
|
-
--fronted) FRONTED=true; shift;;
|
|
26
|
-
*) fail "unknown option $1";;
|
|
27
|
-
esac
|
|
28
|
-
done
|
|
29
|
-
|
|
30
|
-
command -v git >/dev/null 2>&1 || fail "git is required — install it from https://git-scm.com/ and re-run"
|
|
31
|
-
command -v node >/dev/null 2>&1 || fail "node 20 or newer is required — install it from https://nodejs.org/ and re-run"
|
|
32
|
-
command -v npm >/dev/null 2>&1 || fail "npm is required (it ships with node) — install node from https://nodejs.org/ and re-run"
|
|
33
|
-
|
|
34
|
-
major=$(node -v | sed 's/^v//' | cut -d. -f1)
|
|
35
|
-
[ "$major" -ge 20 ] 2>/dev/null || fail "node 20 or newer is required (found $(node -v))"
|
|
36
|
-
|
|
37
|
-
if [ -d "$DIR/.git" ]; then
|
|
38
|
-
say "updating the existing install in $DIR"
|
|
39
|
-
git -C "$DIR" pull --ff-only || fail "could not update $DIR — it has local changes; update it by hand"
|
|
40
|
-
elif [ -e "$DIR" ]; then
|
|
41
|
-
fail "$DIR exists and is not a FediPod checkout — set FEDIPOD_DIR to another location and re-run"
|
|
42
|
-
else
|
|
43
|
-
say "installing into $DIR"
|
|
44
|
-
git clone --depth 1 "$REPO" "$DIR"
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
cd "$DIR"
|
|
48
|
-
npm install --omit=dev --no-audit --no-fund
|
|
49
|
-
|
|
50
|
-
# Put `fedipod` on PATH, the same command an `npm install -g fedipod` gives.
|
|
51
|
-
# Everything printed from here on — the docs, the agent's own advice, the
|
|
52
|
-
# command a signup page hands you — names it that way, and a checkout that
|
|
53
|
-
# skipped this step could run none of them.
|
|
54
|
-
if npm link >/dev/null 2>&1; then
|
|
55
|
-
say "linked: the fedipod command is on your PATH"
|
|
56
|
-
else
|
|
57
|
-
say "note: could not link the fedipod command — run it as: node $DIR/bin/fedipod.mjs"
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
if [ -n "$HANDLE" ]; then
|
|
61
|
-
printf '{"handle":"%s","kind":"%s","pod":"%s","issuer":"%s","gateway":"%s","secret":"%s","fronted":%s}\n' \
|
|
62
|
-
"$HANDLE" "$KIND" "$POD" "$ISSUER" "$GATEWAY" "$SECRET" "$FRONTED" > "$DIR/first-run.json"
|
|
63
|
-
chmod 600 "$DIR/first-run.json"
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
say ""
|
|
67
|
-
say "Installed. To start (first run opens setup in your browser):"
|
|
68
|
-
say ""
|
|
69
|
-
say " fedipod start"
|