@fluid-app/fluid-cli-member-site 0.1.2 → 0.1.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/README.md +14 -5
- package/dist/index.mjs +37 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,11 +38,20 @@ which it is with `--as member` or `--as admin`, and a site of the other owner
|
|
|
38
38
|
kind is refused before any request, with the login it needs. Without `--as`,
|
|
39
39
|
the server decides, and a refusal names the login the site needs.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
`fluid login` signs in admins and reps. An admin who is also a rep of the same
|
|
42
|
+
company builds both kinds of site after one normal `fluid login`: `--as admin`
|
|
43
|
+
builds company sites with the admin session, and `--as member` builds the
|
|
44
|
+
admin's own member sites with a rep session that the identity service narrows
|
|
45
|
+
the same login to, with no second sign-in. The identity service issues that
|
|
46
|
+
rep session only to a person who is a rep in the company. An admin who is not
|
|
47
|
+
one gets "You are not a rep at <company>, so you cannot build a member site
|
|
48
|
+
there. Use --as admin for company sites." before any member API request.
|
|
49
|
+
|
|
50
|
+
Only a login made through the identity service can be narrowed. A legacy login,
|
|
51
|
+
and a token set in `FLUID_TOKEN`, are sent as they are for `--as member` too,
|
|
52
|
+
so an admin on a legacy login is still refused by the member API and builds
|
|
53
|
+
company sites only. A member signs in to this command with the member's own
|
|
54
|
+
token, set in `FLUID_TOKEN`.
|
|
46
55
|
|
|
47
56
|
## Commands
|
|
48
57
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { declareInputs, exitOnInputsError, findProjectConfig, getAuthToken, readConfig, withInternalReadsAsync } from "@fluid-app/fluid-cli";
|
|
3
|
+
import { declareInputs, exitOnInputsError, findProjectConfig, getAuthToken, getRoleAuthTokenAsync, isRoleNotGrantedError, readConfig, withInternalReadsAsync } from "@fluid-app/fluid-cli";
|
|
4
4
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
5
5
|
import http from "node:http";
|
|
6
6
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -555,6 +555,24 @@ function resolveAuthToken() {
|
|
|
555
555
|
return getAuthToken() ?? null;
|
|
556
556
|
}
|
|
557
557
|
/**
|
|
558
|
+
* The stored login's token narrowed to a rep, which is what the member API
|
|
559
|
+
* accepts from a person who is also an admin: the identity service issues it
|
|
560
|
+
* only for a person who holds rep standing in the company. A login that
|
|
561
|
+
* cannot be narrowed, a literal token (`FLUID_TOKEN`) or a legacy profile,
|
|
562
|
+
* gives the token `resolveAuthToken` gives.
|
|
563
|
+
*
|
|
564
|
+
* Throws a MemberSiteCliError, before any member API request, when the person
|
|
565
|
+
* is not a rep in the company.
|
|
566
|
+
*/
|
|
567
|
+
async function resolveRepToken() {
|
|
568
|
+
try {
|
|
569
|
+
return await getRoleAuthTokenAsync(["rep"]) ?? null;
|
|
570
|
+
} catch (error) {
|
|
571
|
+
if (!isRoleNotGrantedError(error)) throw error;
|
|
572
|
+
throw new MemberSiteCliError(`You are not a rep at ${error.companyName ?? "this company"}, so you cannot build a member site there. Use --as admin for company sites.`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
558
576
|
* A client for one login. Reads that fail with 502, 503 or 504 are retried
|
|
559
577
|
* twice, as the theme CLI retries them. A write is never retried, because a
|
|
560
578
|
* failed response may follow a successful change, and a 404 never is,
|
|
@@ -604,9 +622,18 @@ function loginFor(ownerKind) {
|
|
|
604
622
|
function describeLogin(kind) {
|
|
605
623
|
return kind === "member" ? "a member login" : "an admin login";
|
|
606
624
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
625
|
+
const defaultSources = {
|
|
626
|
+
token: resolveAuthToken,
|
|
627
|
+
repToken: resolveRepToken
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* The login a command acts with, or an error that says to sign in. With
|
|
631
|
+
* `--as member` the token is the stored login's rep session, so an admin who
|
|
632
|
+
* is also a rep builds their own sites with the login they already have;
|
|
633
|
+
* otherwise it is the stored login's token as it is.
|
|
634
|
+
*/
|
|
635
|
+
async function loadLogin(kind, sources = defaultSources) {
|
|
636
|
+
const token = kind === "member" ? await sources.repToken() : sources.token();
|
|
610
637
|
if (!token) throw new MemberSiteCliError("Not logged in. Run `fluid login` first (or set FLUID_TOKEN).");
|
|
611
638
|
return {
|
|
612
639
|
token,
|
|
@@ -749,7 +776,7 @@ async function runCreate(handle, options, deps = defaultDeps$5) {
|
|
|
749
776
|
assertValidHandle(handle);
|
|
750
777
|
const name = options.name?.trim() || nameFromHandle(handle);
|
|
751
778
|
const ownerKind = ownerKindOf(options);
|
|
752
|
-
const login = deps.loadLogin(parseLoginKind(options.as));
|
|
779
|
+
const login = await deps.loadLogin(parseLoginKind(options.as));
|
|
753
780
|
const site = await clientForOwner(ownerKind, login, deps.createClients(login.token)).createSite({
|
|
754
781
|
name,
|
|
755
782
|
handle
|
|
@@ -1015,7 +1042,7 @@ var PreviewProxy = class {
|
|
|
1015
1042
|
return fetch(`${this.previewUrl}${path}`, {
|
|
1016
1043
|
method: "GET",
|
|
1017
1044
|
headers: {
|
|
1018
|
-
authorization: `Bearer ${this.options.getToken()}`,
|
|
1045
|
+
authorization: `Bearer ${await this.options.getToken()}`,
|
|
1019
1046
|
accept: route.kind === "page" ? "text/html" : "*/*",
|
|
1020
1047
|
"accept-encoding": "identity",
|
|
1021
1048
|
"user-agent": "Fluid CLI"
|
|
@@ -1359,7 +1386,7 @@ async function openBoundSite(dirArg, as, deps, context) {
|
|
|
1359
1386
|
if (!stored) throw new MemberSiteCliError(`${dir} is not a pulled site: it has no readable ${BINDING_FILE}. Pull the site first: fluid member-site pull <site> [dir]`);
|
|
1360
1387
|
const binding = structuredClone(stored);
|
|
1361
1388
|
context.ownerKind = binding.owner_kind;
|
|
1362
|
-
const login = deps.loadLogin(parseLoginKind(as));
|
|
1389
|
+
const login = await deps.loadLogin(parseLoginKind(as));
|
|
1363
1390
|
const api = clientForOwner(binding.owner_kind, login, deps.createClients(login.token));
|
|
1364
1391
|
if (binding.api_base !== getApiBase()) throw new MemberSiteCliError(`${dir} was pulled from ${binding.api_base}, and this login is for ${getApiBase()}. Use the login it was pulled with, or pull the site again.`);
|
|
1365
1392
|
return {
|
|
@@ -2717,7 +2744,7 @@ async function runDev(dirArg, options, deps = defaultDeps$2, context = {}) {
|
|
|
2717
2744
|
ownerKind: binding.owner_kind
|
|
2718
2745
|
},
|
|
2719
2746
|
apiBase,
|
|
2720
|
-
getToken: () => deps.loadLogin(parseLoginKind(options.as)).token
|
|
2747
|
+
getToken: async () => (await deps.loadLogin(parseLoginKind(options.as))).token
|
|
2721
2748
|
});
|
|
2722
2749
|
if (await proxy.probe() === "unavailable") throw new MemberSiteCliError(DRAFT_PREVIEW_UNAVAILABLE);
|
|
2723
2750
|
await checkPortAvailable(DEV_HOST, port);
|
|
@@ -4972,7 +4999,7 @@ const defaultDeps$1 = {
|
|
|
4972
4999
|
log: (line) => console.log(line)
|
|
4973
5000
|
};
|
|
4974
5001
|
async function runList(options, deps = defaultDeps$1) {
|
|
4975
|
-
const login = deps.loadLogin(parseLoginKind(options.as));
|
|
5002
|
+
const login = await deps.loadLogin(parseLoginKind(options.as));
|
|
4976
5003
|
const listing = await listForLogin(login, deps.createClients(login.token));
|
|
4977
5004
|
for (const line of formatListing(listing)) deps.log(line);
|
|
4978
5005
|
}
|
|
@@ -5050,7 +5077,7 @@ function formatPull(result) {
|
|
|
5050
5077
|
* reported with the login the site needs.
|
|
5051
5078
|
*/
|
|
5052
5079
|
async function runPull(ref, dirArg, options, deps = defaultDeps, context = {}) {
|
|
5053
|
-
const login = deps.loadLogin(parseLoginKind(options.as));
|
|
5080
|
+
const login = await deps.loadLogin(parseLoginKind(options.as));
|
|
5054
5081
|
const clients = deps.createClients(login.token);
|
|
5055
5082
|
const known = await readBinding(resolve(dirArg ?? ref));
|
|
5056
5083
|
if (known && (known.site_id === ref || known.handle === ref)) {
|