@fluxfiles/node 0.1.26 → 0.1.27
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/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +55 -5
- package/dist/index.mjs +55 -5
- package/package.json +1 -1
- package/src/token.ts +46 -5
- package/src/types.ts +10 -0
package/dist/index.d.mts
CHANGED
|
@@ -143,6 +143,12 @@ interface BaseTokenOptions {
|
|
|
143
143
|
/** Edition preset that defaults a tier's claims (`pro`/`agency`/`enterprise`).
|
|
144
144
|
* DX sugar — explicit claim options still win, and the license gates the code. */
|
|
145
145
|
edition?: 'free' | 'pro' | 'agency' | 'enterprise';
|
|
146
|
+
/** Role preset that defaults a person's capability level (perms, owner-scoping,
|
|
147
|
+
* and free power-user toggles) — orthogonal to `edition`, which defaults which
|
|
148
|
+
* *paid features* a tier gets. DX sugar — explicit claim options still win, and
|
|
149
|
+
* `role` never itself becomes a JWT claim. See docs/ACL-ROLE-PRESETS-DESIGN.md.
|
|
150
|
+
* Not supported on BYOB tokens (matches `edition`'s existing BYOB exclusion). */
|
|
151
|
+
role?: 'viewer' | 'editor' | 'admin' | 'superadmin';
|
|
146
152
|
/** Paid-module claims (3-layer gate: code installed + licensed + this claim).
|
|
147
153
|
* All default off; inert unless the module is installed & licensed on the server. */
|
|
148
154
|
allowShare?: boolean;
|
|
@@ -157,6 +163,10 @@ interface BaseTokenOptions {
|
|
|
157
163
|
/** Allow the share landing to render an inline preview (images via /api/fm/img;
|
|
158
164
|
* PDFs only on uncapped shares). Default true; false = download-only. */
|
|
159
165
|
sharePreview?: boolean;
|
|
166
|
+
/** Opt-in per-event analytics on a share link (visitor IP/user-agent persisted
|
|
167
|
+
* to `_fluxfiles/share-events/<jti>.jsonl`) — kept separate from `allowShare`
|
|
168
|
+
* since it's a real privacy footprint. Default false. */
|
|
169
|
+
shareAnalytics?: boolean;
|
|
160
170
|
allowIntake?: boolean;
|
|
161
171
|
/** Public base the intake create response builds the portal link from
|
|
162
172
|
* (e.g. `https://files.acme.com/public/intake.html`). http(s) only; empty = the
|
package/dist/index.d.ts
CHANGED
|
@@ -143,6 +143,12 @@ interface BaseTokenOptions {
|
|
|
143
143
|
/** Edition preset that defaults a tier's claims (`pro`/`agency`/`enterprise`).
|
|
144
144
|
* DX sugar — explicit claim options still win, and the license gates the code. */
|
|
145
145
|
edition?: 'free' | 'pro' | 'agency' | 'enterprise';
|
|
146
|
+
/** Role preset that defaults a person's capability level (perms, owner-scoping,
|
|
147
|
+
* and free power-user toggles) — orthogonal to `edition`, which defaults which
|
|
148
|
+
* *paid features* a tier gets. DX sugar — explicit claim options still win, and
|
|
149
|
+
* `role` never itself becomes a JWT claim. See docs/ACL-ROLE-PRESETS-DESIGN.md.
|
|
150
|
+
* Not supported on BYOB tokens (matches `edition`'s existing BYOB exclusion). */
|
|
151
|
+
role?: 'viewer' | 'editor' | 'admin' | 'superadmin';
|
|
146
152
|
/** Paid-module claims (3-layer gate: code installed + licensed + this claim).
|
|
147
153
|
* All default off; inert unless the module is installed & licensed on the server. */
|
|
148
154
|
allowShare?: boolean;
|
|
@@ -157,6 +163,10 @@ interface BaseTokenOptions {
|
|
|
157
163
|
/** Allow the share landing to render an inline preview (images via /api/fm/img;
|
|
158
164
|
* PDFs only on uncapped shares). Default true; false = download-only. */
|
|
159
165
|
sharePreview?: boolean;
|
|
166
|
+
/** Opt-in per-event analytics on a share link (visitor IP/user-agent persisted
|
|
167
|
+
* to `_fluxfiles/share-events/<jti>.jsonl`) — kept separate from `allowShare`
|
|
168
|
+
* since it's a real privacy footprint. Default false. */
|
|
169
|
+
shareAnalytics?: boolean;
|
|
160
170
|
allowIntake?: boolean;
|
|
161
171
|
/** Public base the intake create response builds the portal link from
|
|
162
172
|
* (e.g. `https://files.acme.com/public/intake.html`). http(s) only; empty = the
|
package/dist/index.js
CHANGED
|
@@ -84,12 +84,13 @@ function newJti() {
|
|
|
84
84
|
function createToken(opts) {
|
|
85
85
|
const secret = resolveSecret(opts.secret);
|
|
86
86
|
const now = Math.floor(Date.now() / 1e3);
|
|
87
|
+
const rolePreset = opts.role ? ROLE_PRESETS[String(opts.role).toLowerCase()] : void 0;
|
|
87
88
|
const payload = {
|
|
88
89
|
sub: opts.userId,
|
|
89
90
|
iat: now,
|
|
90
91
|
exp: now + (opts.ttl ?? 3600),
|
|
91
92
|
jti: newJti(),
|
|
92
|
-
perms: opts.perms ?? ["read"],
|
|
93
|
+
perms: opts.perms ?? (rolePreset == null ? void 0 : rolePreset.perms) ?? ["read"],
|
|
93
94
|
disks: opts.disks ?? ["local"],
|
|
94
95
|
prefix: opts.prefix ?? "",
|
|
95
96
|
max_upload: opts.maxUploadMb ?? 10,
|
|
@@ -97,8 +98,8 @@ function createToken(opts) {
|
|
|
97
98
|
max_storage: opts.maxStorageMb ?? 0,
|
|
98
99
|
max_files: opts.maxFiles ?? 0
|
|
99
100
|
};
|
|
100
|
-
if (opts.ownerOnly) payload.owner_only = true;
|
|
101
|
-
applyTenantOverrides(payload, opts);
|
|
101
|
+
if (opts.ownerOnly ?? (rolePreset == null ? void 0 : rolePreset.owner_only)) payload.owner_only = true;
|
|
102
|
+
applyTenantOverrides(payload, opts, rolePreset);
|
|
102
103
|
return sign(payload, secret);
|
|
103
104
|
}
|
|
104
105
|
function createByobToken(opts) {
|
|
@@ -139,13 +140,61 @@ function sanitizeVariants(v) {
|
|
|
139
140
|
var EDITION_PRESETS = {
|
|
140
141
|
pro: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
141
142
|
agency: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
142
|
-
|
|
143
|
+
studio: {
|
|
144
|
+
allow_optimize: true,
|
|
145
|
+
allow_share: true,
|
|
146
|
+
allow_intake: true,
|
|
147
|
+
allow_versioning: true,
|
|
148
|
+
allow_webhooks: true,
|
|
149
|
+
allow_ai_vision: true,
|
|
150
|
+
allow_ocr: true
|
|
151
|
+
},
|
|
152
|
+
enterprise: {
|
|
153
|
+
allow_optimize: true,
|
|
154
|
+
allow_share: true,
|
|
155
|
+
allow_intake: true,
|
|
156
|
+
allow_versioning: true,
|
|
157
|
+
allow_webhooks: true,
|
|
158
|
+
allow_ai_vision: true,
|
|
159
|
+
allow_ocr: true,
|
|
160
|
+
allow_virus_scan: true,
|
|
161
|
+
allow_c2pa: true,
|
|
162
|
+
allow_backup: true,
|
|
163
|
+
allow_audit_export: true,
|
|
164
|
+
allow_dlp_scan: true,
|
|
165
|
+
allow_legal_hold: true
|
|
166
|
+
}
|
|
143
167
|
};
|
|
144
|
-
|
|
168
|
+
var ROLE_PRESETS = {
|
|
169
|
+
viewer: { perms: ["read"], owner_only: true, allow_extract: false, allow_chmod: false },
|
|
170
|
+
editor: { perms: ["read", "write"], owner_only: true, allow_extract: true, allow_chmod: false },
|
|
171
|
+
admin: {
|
|
172
|
+
perms: ["read", "write", "delete", "audit"],
|
|
173
|
+
owner_only: false,
|
|
174
|
+
allow_extract: true,
|
|
175
|
+
allow_chmod: true,
|
|
176
|
+
allow_code_edit: true,
|
|
177
|
+
show_hidden: true
|
|
178
|
+
},
|
|
179
|
+
superadmin: {
|
|
180
|
+
perms: ["read", "write", "delete", "audit"],
|
|
181
|
+
owner_only: false,
|
|
182
|
+
allow_extract: true,
|
|
183
|
+
allow_chmod: true,
|
|
184
|
+
allow_code_edit: true,
|
|
185
|
+
show_hidden: true
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
function applyTenantOverrides(payload, opts, rolePreset) {
|
|
145
189
|
const preset = opts.edition ? EDITION_PRESETS[String(opts.edition).toLowerCase()] : void 0;
|
|
146
190
|
if (preset) {
|
|
147
191
|
for (const [k, v] of Object.entries(preset)) if (!(k in payload)) payload[k] = v;
|
|
148
192
|
}
|
|
193
|
+
if (rolePreset) {
|
|
194
|
+
for (const [k, v] of Object.entries(rolePreset)) {
|
|
195
|
+
if (k !== "perms" && k !== "owner_only" && !(k in payload)) payload[k] = v;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
149
198
|
if (opts.aiAutoTag !== void 0) payload.ai_auto_tag = !!opts.aiAutoTag;
|
|
150
199
|
if (opts.rateRead && opts.rateRead > 0) payload.rate_read = Math.trunc(opts.rateRead);
|
|
151
200
|
if (opts.rateWrite && opts.rateWrite > 0) payload.rate_write = Math.trunc(opts.rateWrite);
|
|
@@ -181,6 +230,7 @@ function applyTenantOverrides(payload, opts) {
|
|
|
181
230
|
if (opts.shareUrlTtl && opts.shareUrlTtl > 0) payload.share_url_ttl = Math.trunc(opts.shareUrlTtl);
|
|
182
231
|
if (opts.shareBaseUrl) payload.share_base_url = String(opts.shareBaseUrl);
|
|
183
232
|
if (opts.sharePreview !== void 0) payload.share_preview = !!opts.sharePreview;
|
|
233
|
+
if (opts.shareAnalytics !== void 0) payload.share_analytics = !!opts.shareAnalytics;
|
|
184
234
|
if (opts.allowIntake !== void 0) payload.allow_intake = !!opts.allowIntake;
|
|
185
235
|
if (opts.intakeBaseUrl) payload.intake_base_url = String(opts.intakeBaseUrl);
|
|
186
236
|
if (opts.allowVersioning !== void 0) payload.allow_versioning = !!opts.allowVersioning;
|
package/dist/index.mjs
CHANGED
|
@@ -62,12 +62,13 @@ function newJti() {
|
|
|
62
62
|
function createToken(opts) {
|
|
63
63
|
const secret = resolveSecret(opts.secret);
|
|
64
64
|
const now = Math.floor(Date.now() / 1e3);
|
|
65
|
+
const rolePreset = opts.role ? ROLE_PRESETS[String(opts.role).toLowerCase()] : void 0;
|
|
65
66
|
const payload = {
|
|
66
67
|
sub: opts.userId,
|
|
67
68
|
iat: now,
|
|
68
69
|
exp: now + (opts.ttl ?? 3600),
|
|
69
70
|
jti: newJti(),
|
|
70
|
-
perms: opts.perms ?? ["read"],
|
|
71
|
+
perms: opts.perms ?? (rolePreset == null ? void 0 : rolePreset.perms) ?? ["read"],
|
|
71
72
|
disks: opts.disks ?? ["local"],
|
|
72
73
|
prefix: opts.prefix ?? "",
|
|
73
74
|
max_upload: opts.maxUploadMb ?? 10,
|
|
@@ -75,8 +76,8 @@ function createToken(opts) {
|
|
|
75
76
|
max_storage: opts.maxStorageMb ?? 0,
|
|
76
77
|
max_files: opts.maxFiles ?? 0
|
|
77
78
|
};
|
|
78
|
-
if (opts.ownerOnly) payload.owner_only = true;
|
|
79
|
-
applyTenantOverrides(payload, opts);
|
|
79
|
+
if (opts.ownerOnly ?? (rolePreset == null ? void 0 : rolePreset.owner_only)) payload.owner_only = true;
|
|
80
|
+
applyTenantOverrides(payload, opts, rolePreset);
|
|
80
81
|
return sign(payload, secret);
|
|
81
82
|
}
|
|
82
83
|
function createByobToken(opts) {
|
|
@@ -117,13 +118,61 @@ function sanitizeVariants(v) {
|
|
|
117
118
|
var EDITION_PRESETS = {
|
|
118
119
|
pro: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
119
120
|
agency: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
120
|
-
|
|
121
|
+
studio: {
|
|
122
|
+
allow_optimize: true,
|
|
123
|
+
allow_share: true,
|
|
124
|
+
allow_intake: true,
|
|
125
|
+
allow_versioning: true,
|
|
126
|
+
allow_webhooks: true,
|
|
127
|
+
allow_ai_vision: true,
|
|
128
|
+
allow_ocr: true
|
|
129
|
+
},
|
|
130
|
+
enterprise: {
|
|
131
|
+
allow_optimize: true,
|
|
132
|
+
allow_share: true,
|
|
133
|
+
allow_intake: true,
|
|
134
|
+
allow_versioning: true,
|
|
135
|
+
allow_webhooks: true,
|
|
136
|
+
allow_ai_vision: true,
|
|
137
|
+
allow_ocr: true,
|
|
138
|
+
allow_virus_scan: true,
|
|
139
|
+
allow_c2pa: true,
|
|
140
|
+
allow_backup: true,
|
|
141
|
+
allow_audit_export: true,
|
|
142
|
+
allow_dlp_scan: true,
|
|
143
|
+
allow_legal_hold: true
|
|
144
|
+
}
|
|
121
145
|
};
|
|
122
|
-
|
|
146
|
+
var ROLE_PRESETS = {
|
|
147
|
+
viewer: { perms: ["read"], owner_only: true, allow_extract: false, allow_chmod: false },
|
|
148
|
+
editor: { perms: ["read", "write"], owner_only: true, allow_extract: true, allow_chmod: false },
|
|
149
|
+
admin: {
|
|
150
|
+
perms: ["read", "write", "delete", "audit"],
|
|
151
|
+
owner_only: false,
|
|
152
|
+
allow_extract: true,
|
|
153
|
+
allow_chmod: true,
|
|
154
|
+
allow_code_edit: true,
|
|
155
|
+
show_hidden: true
|
|
156
|
+
},
|
|
157
|
+
superadmin: {
|
|
158
|
+
perms: ["read", "write", "delete", "audit"],
|
|
159
|
+
owner_only: false,
|
|
160
|
+
allow_extract: true,
|
|
161
|
+
allow_chmod: true,
|
|
162
|
+
allow_code_edit: true,
|
|
163
|
+
show_hidden: true
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
function applyTenantOverrides(payload, opts, rolePreset) {
|
|
123
167
|
const preset = opts.edition ? EDITION_PRESETS[String(opts.edition).toLowerCase()] : void 0;
|
|
124
168
|
if (preset) {
|
|
125
169
|
for (const [k, v] of Object.entries(preset)) if (!(k in payload)) payload[k] = v;
|
|
126
170
|
}
|
|
171
|
+
if (rolePreset) {
|
|
172
|
+
for (const [k, v] of Object.entries(rolePreset)) {
|
|
173
|
+
if (k !== "perms" && k !== "owner_only" && !(k in payload)) payload[k] = v;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
127
176
|
if (opts.aiAutoTag !== void 0) payload.ai_auto_tag = !!opts.aiAutoTag;
|
|
128
177
|
if (opts.rateRead && opts.rateRead > 0) payload.rate_read = Math.trunc(opts.rateRead);
|
|
129
178
|
if (opts.rateWrite && opts.rateWrite > 0) payload.rate_write = Math.trunc(opts.rateWrite);
|
|
@@ -159,6 +208,7 @@ function applyTenantOverrides(payload, opts) {
|
|
|
159
208
|
if (opts.shareUrlTtl && opts.shareUrlTtl > 0) payload.share_url_ttl = Math.trunc(opts.shareUrlTtl);
|
|
160
209
|
if (opts.shareBaseUrl) payload.share_base_url = String(opts.shareBaseUrl);
|
|
161
210
|
if (opts.sharePreview !== void 0) payload.share_preview = !!opts.sharePreview;
|
|
211
|
+
if (opts.shareAnalytics !== void 0) payload.share_analytics = !!opts.shareAnalytics;
|
|
162
212
|
if (opts.allowIntake !== void 0) payload.allow_intake = !!opts.allowIntake;
|
|
163
213
|
if (opts.intakeBaseUrl) payload.intake_base_url = String(opts.intakeBaseUrl);
|
|
164
214
|
if (opts.allowVersioning !== void 0) payload.allow_versioning = !!opts.allowVersioning;
|
package/package.json
CHANGED
package/src/token.ts
CHANGED
|
@@ -34,12 +34,16 @@ function newJti(): string {
|
|
|
34
34
|
export function createToken(opts: CreateTokenOptions): string {
|
|
35
35
|
const secret = resolveSecret(opts.secret);
|
|
36
36
|
const now = Math.floor(Date.now() / 1000);
|
|
37
|
+
// Role preset (DX sugar, docs/ACL-ROLE-PRESETS-DESIGN.md): resolved BEFORE the base
|
|
38
|
+
// payload object, because `perms` already has an unconditional default baked into
|
|
39
|
+
// that object below — a plain "set if absent" guard would never fire for it.
|
|
40
|
+
const rolePreset = opts.role ? ROLE_PRESETS[String(opts.role).toLowerCase()] : undefined;
|
|
37
41
|
const payload: Record<string, unknown> = {
|
|
38
42
|
sub: opts.userId,
|
|
39
43
|
iat: now,
|
|
40
44
|
exp: now + (opts.ttl ?? 3600),
|
|
41
45
|
jti: newJti(),
|
|
42
|
-
perms: opts.perms ?? ['read'],
|
|
46
|
+
perms: opts.perms ?? (rolePreset?.perms as string[] | undefined) ?? ['read'],
|
|
43
47
|
disks: opts.disks ?? ['local'],
|
|
44
48
|
prefix: opts.prefix ?? '',
|
|
45
49
|
max_upload: opts.maxUploadMb ?? 10,
|
|
@@ -47,8 +51,8 @@ export function createToken(opts: CreateTokenOptions): string {
|
|
|
47
51
|
max_storage: opts.maxStorageMb ?? 0,
|
|
48
52
|
max_files: opts.maxFiles ?? 0,
|
|
49
53
|
};
|
|
50
|
-
if (opts.ownerOnly) payload.owner_only = true;
|
|
51
|
-
applyTenantOverrides(payload, opts);
|
|
54
|
+
if (opts.ownerOnly ?? (rolePreset?.owner_only as boolean | undefined)) payload.owner_only = true;
|
|
55
|
+
applyTenantOverrides(payload, opts, rolePreset);
|
|
52
56
|
return sign(payload, secret);
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -101,13 +105,49 @@ function sanitizeVariants(v: BaseTokenOptions['variants']): Record<string, numbe
|
|
|
101
105
|
const EDITION_PRESETS: Record<string, Record<string, boolean>> = {
|
|
102
106
|
pro: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
103
107
|
agency: { allow_optimize: true, allow_share: true, allow_intake: true },
|
|
104
|
-
|
|
108
|
+
studio: {
|
|
109
|
+
allow_optimize: true, allow_share: true, allow_intake: true,
|
|
110
|
+
allow_versioning: true, allow_webhooks: true, allow_ai_vision: true, allow_ocr: true,
|
|
111
|
+
},
|
|
112
|
+
enterprise: {
|
|
113
|
+
allow_optimize: true, allow_share: true, allow_intake: true,
|
|
114
|
+
allow_versioning: true, allow_webhooks: true, allow_ai_vision: true, allow_ocr: true,
|
|
115
|
+
allow_virus_scan: true, allow_c2pa: true, allow_backup: true, allow_audit_export: true,
|
|
116
|
+
allow_dlp_scan: true, allow_legal_hold: true,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** Role preset (DX sugar, docs/ACL-ROLE-PRESETS-DESIGN.md): default a person's
|
|
121
|
+
* capability level; explicit opts always win. `role` never itself becomes a JWT
|
|
122
|
+
* claim — it only ever expands into ordinary claims decoded server-side already. */
|
|
123
|
+
const ROLE_PRESETS: Record<string, Record<string, unknown>> = {
|
|
124
|
+
viewer: { perms: ['read'], owner_only: true, allow_extract: false, allow_chmod: false },
|
|
125
|
+
editor: { perms: ['read', 'write'], owner_only: true, allow_extract: true, allow_chmod: false },
|
|
126
|
+
admin: {
|
|
127
|
+
perms: ['read', 'write', 'delete', 'audit'], owner_only: false,
|
|
128
|
+
allow_extract: true, allow_chmod: true, allow_code_edit: true, show_hidden: true,
|
|
129
|
+
},
|
|
130
|
+
superadmin: {
|
|
131
|
+
perms: ['read', 'write', 'delete', 'audit'], owner_only: false,
|
|
132
|
+
allow_extract: true, allow_chmod: true, allow_code_edit: true, show_hidden: true,
|
|
133
|
+
},
|
|
105
134
|
};
|
|
106
135
|
|
|
107
136
|
/** Copy the optional per-tenant override claims into a payload when set. */
|
|
108
|
-
function applyTenantOverrides(
|
|
137
|
+
function applyTenantOverrides(
|
|
138
|
+
payload: Record<string, unknown>,
|
|
139
|
+
opts: BaseTokenOptions,
|
|
140
|
+
rolePreset?: Record<string, unknown>,
|
|
141
|
+
): void {
|
|
109
142
|
const preset = opts.edition ? EDITION_PRESETS[String(opts.edition).toLowerCase()] : undefined;
|
|
110
143
|
if (preset) for (const [k, v] of Object.entries(preset)) if (!(k in payload)) payload[k] = v;
|
|
144
|
+
// Role preset: the rest of the bundle, excluding `perms`/`owner_only` — both are
|
|
145
|
+
// already resolved above (in createToken()), before this function ever runs.
|
|
146
|
+
if (rolePreset) {
|
|
147
|
+
for (const [k, v] of Object.entries(rolePreset)) {
|
|
148
|
+
if (k !== 'perms' && k !== 'owner_only' && !(k in payload)) payload[k] = v;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
111
151
|
if (opts.aiAutoTag !== undefined) payload.ai_auto_tag = !!opts.aiAutoTag;
|
|
112
152
|
if (opts.rateRead && opts.rateRead > 0) payload.rate_read = Math.trunc(opts.rateRead);
|
|
113
153
|
if (opts.rateWrite && opts.rateWrite > 0) payload.rate_write = Math.trunc(opts.rateWrite);
|
|
@@ -156,6 +196,7 @@ function applyTenantOverrides(payload: Record<string, unknown>, opts: BaseTokenO
|
|
|
156
196
|
if (opts.shareUrlTtl && opts.shareUrlTtl > 0) payload.share_url_ttl = Math.trunc(opts.shareUrlTtl);
|
|
157
197
|
if (opts.shareBaseUrl) payload.share_base_url = String(opts.shareBaseUrl);
|
|
158
198
|
if (opts.sharePreview !== undefined) payload.share_preview = !!opts.sharePreview;
|
|
199
|
+
if (opts.shareAnalytics !== undefined) payload.share_analytics = !!opts.shareAnalytics;
|
|
159
200
|
if (opts.allowIntake !== undefined) payload.allow_intake = !!opts.allowIntake;
|
|
160
201
|
// Intake portal link base — same shape as shareBaseUrl (the core drops a
|
|
161
202
|
// non-http(s) value on decode).
|
package/src/types.ts
CHANGED
|
@@ -147,6 +147,12 @@ export interface BaseTokenOptions {
|
|
|
147
147
|
/** Edition preset that defaults a tier's claims (`pro`/`agency`/`enterprise`).
|
|
148
148
|
* DX sugar — explicit claim options still win, and the license gates the code. */
|
|
149
149
|
edition?: 'free' | 'pro' | 'agency' | 'enterprise';
|
|
150
|
+
/** Role preset that defaults a person's capability level (perms, owner-scoping,
|
|
151
|
+
* and free power-user toggles) — orthogonal to `edition`, which defaults which
|
|
152
|
+
* *paid features* a tier gets. DX sugar — explicit claim options still win, and
|
|
153
|
+
* `role` never itself becomes a JWT claim. See docs/ACL-ROLE-PRESETS-DESIGN.md.
|
|
154
|
+
* Not supported on BYOB tokens (matches `edition`'s existing BYOB exclusion). */
|
|
155
|
+
role?: 'viewer' | 'editor' | 'admin' | 'superadmin';
|
|
150
156
|
/** Paid-module claims (3-layer gate: code installed + licensed + this claim).
|
|
151
157
|
* All default off; inert unless the module is installed & licensed on the server. */
|
|
152
158
|
allowShare?: boolean;
|
|
@@ -161,6 +167,10 @@ export interface BaseTokenOptions {
|
|
|
161
167
|
/** Allow the share landing to render an inline preview (images via /api/fm/img;
|
|
162
168
|
* PDFs only on uncapped shares). Default true; false = download-only. */
|
|
163
169
|
sharePreview?: boolean;
|
|
170
|
+
/** Opt-in per-event analytics on a share link (visitor IP/user-agent persisted
|
|
171
|
+
* to `_fluxfiles/share-events/<jti>.jsonl`) — kept separate from `allowShare`
|
|
172
|
+
* since it's a real privacy footprint. Default false. */
|
|
173
|
+
shareAnalytics?: boolean;
|
|
164
174
|
allowIntake?: boolean;
|
|
165
175
|
/** Public base the intake create response builds the portal link from
|
|
166
176
|
* (e.g. `https://files.acme.com/public/intake.html`). http(s) only; empty = the
|