@getxflow/cli 0.9.1 → 0.10.1
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/bin.js +41 -1
- package/dist/commands/skills.js +119 -21
- package/dist/commands/storage.js +370 -0
- package/dist/commands/update.js +6 -4
- package/dist/help.js +71 -2
- package/dist/state.js +36 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +170 -134
package/skills/xflow/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: xflow
|
|
3
|
-
description: Build, deploy and publish apps on the XFlow platform with the xflow CLI. Use whenever the project root has xflow.json or VITE_XFLOW_* variables, and for any task that touches deployment, publishing, rollback, source sync, the project database or SQL migrations, cloud functions, schedules, environment variables and secrets, production errors and logs, or UI built on the platform design system.
|
|
3
|
+
description: Build, deploy and publish apps on the XFlow platform with the xflow CLI. Use whenever the project root has xflow.json or VITE_XFLOW_* variables, and for any task that touches deployment, publishing, rollback, source sync, the project database or SQL migrations, cloud functions, schedules, environment variables and secrets, file storage and uploads of photos, video or PDFs, connected accounts of external services, production errors and logs, or UI built on the platform design system.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# XFlow
|
|
@@ -10,6 +10,11 @@ machine, `xflow deploy` sends the sources, and the platform builds them in a cle
|
|
|
10
10
|
sandbox and serves the result as static files. A platform project is recognized by
|
|
11
11
|
the `xflow.json` file in its root.
|
|
12
12
|
|
|
13
|
+
No `xflow.json` means the folder is not bound to a project yet. `xflow projects list`
|
|
14
|
+
names the projects of the organization with their ids, `xflow link <id>` binds the
|
|
15
|
+
folder to one of them (an empty folder also gets the sources), and `xflow init` starts
|
|
16
|
+
a new project from the platform template.
|
|
17
|
+
|
|
13
18
|
## First rule
|
|
14
19
|
|
|
15
20
|
Check commands and flags against `xflow help` and `xflow help <command>`, not against
|
|
@@ -17,9 +22,23 @@ memory. If a command is not in the help output, it does not exist: guessing flag
|
|
|
17
22
|
pointless. The CLI prints a hint with almost every error, read it in full, it usually
|
|
18
23
|
contains the fix.
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
## Keeping these instructions current
|
|
26
|
+
|
|
27
|
+
These instructions ship with xflow CLI 0.10.1. They travel inside the package, so the copy
|
|
28
|
+
you are reading can be older than the CLI answering your commands, and nothing about that
|
|
29
|
+
is visible in the text itself.
|
|
30
|
+
|
|
31
|
+
When the CLI says this file is out of date, run `xflow skills --refresh` and then **read
|
|
32
|
+
again the file it names**. Refreshing only writes new text to disk; until you re-read it you
|
|
33
|
+
are still working from the old rules, and the command will look like it did nothing. This
|
|
34
|
+
repair is yours to make, no need to ask anyone.
|
|
35
|
+
|
|
36
|
+
Updating the CLI itself is a different matter and not yours by default. `xflow update`
|
|
37
|
+
replaces a program shared by every project on the machine, and in most installations it
|
|
38
|
+
cannot even do that: it prints the command for a person instead. So when a newer version is
|
|
39
|
+
merely available, finish the task and offer the update in your summary. The one exception is
|
|
40
|
+
a platform refusing to work with the CLI as too old: nothing runs until it is updated, so say
|
|
41
|
+
what you are doing and run it.
|
|
23
42
|
|
|
24
43
|
## Hard rules
|
|
25
44
|
|
|
@@ -39,15 +58,22 @@ the error points away from the cause. The sections below carry the details.
|
|
|
39
58
|
nothing. A schema change is always a new file.
|
|
40
59
|
5. When inserts start failing with `db_write_locked`, the database is over its plan
|
|
41
60
|
size. The fix is a migration that deletes data, never a rewrite of the failing SQL.
|
|
42
|
-
6. In file storage, call `confirm` only after the PUT has finished
|
|
43
|
-
|
|
61
|
+
6. In file storage, call `confirm` only after the PUT has finished: the platform reads
|
|
62
|
+
the real size from storage, so an early call answers that the file is not there.
|
|
63
|
+
7. Heavy media does not travel with the sources. Photos, video and PDFs go up with
|
|
64
|
+
`xflow storage push`, not into the repository: the archive is capped at 10 MB.
|
|
44
65
|
|
|
45
66
|
## Plan limits
|
|
46
67
|
|
|
47
68
|
The organization runs on a plan with finite limits: projects, cloud functions, developer
|
|
48
|
-
and staff seats, database and file storage, function minutes per month, plus
|
|
49
|
-
|
|
50
|
-
reading it before a long task is cheaper than hitting a wall mid-way.
|
|
69
|
+
and staff seats, database and file storage, function minutes per month, plus schedules and
|
|
70
|
+
how often they may run. `xflow whoami` prints every one of them next to what is already
|
|
71
|
+
used, and reading it before a long task is cheaper than hitting a wall mid-way.
|
|
72
|
+
|
|
73
|
+
The same output names the rights of your own key, which is the other half of the answer:
|
|
74
|
+
destroying data in a migration, deleting files from storage and linking a connected
|
|
75
|
+
account each need a right that is off by default. Reading that line first turns a refusal
|
|
76
|
+
halfway through the work into a sentence said before it starts.
|
|
51
77
|
|
|
52
78
|
A limit refusal is not a bad request. The CLI prints a line starting with `Plan limit:`,
|
|
53
79
|
the API answers `code: "forbidden"` with a `limit` object (`code`, `used`, `limit`), and
|
|
@@ -55,17 +81,21 @@ MCP tools carry the same field. Retrying the command, renaming things or rewriti
|
|
|
55
81
|
code changes nothing: tell the user what ran out and stop. Only the owner or an admin
|
|
56
82
|
lifts it, in the web interface, by freeing the resource or moving to a bigger plan.
|
|
57
83
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
A schedule refused for running too often is one of these too, not a bad cron string: the
|
|
85
|
+
minimum interval comes from the plan, so widen the interval or say what the plan allows.
|
|
86
|
+
The number is in `xflow whoami`, never guess it.
|
|
87
|
+
|
|
88
|
+
On `db_write_locked` (hard rule 5) it is Postgres itself refusing, not the platform. Reads
|
|
89
|
+
and deletes keep working, which is what makes a deleting migration the way out. Write comes
|
|
90
|
+
back within an hour of the data going under the limit.
|
|
62
91
|
|
|
63
92
|
## Workflow
|
|
64
93
|
|
|
65
94
|
1. Change the code.
|
|
66
95
|
2. `npm run typecheck` for a two-second type check (older projects may not have the
|
|
67
96
|
script, then `npx tsc --noEmit`).
|
|
68
|
-
3. `npm run build` if the change is substantial
|
|
97
|
+
3. `npm run build` if the change is substantial. The platform builds again in its own
|
|
98
|
+
sandbox, on one Node version for everyone, so this is only a fast way to see errors early.
|
|
69
99
|
4. `xflow deploy` sends the sources, ships the cloud functions and builds the application
|
|
70
100
|
on the platform, printing each phase and the six-digit number of the version it built.
|
|
71
101
|
5. Give the user the project link the CLI printed and let them look. Do not open a
|
|
@@ -77,25 +107,26 @@ Until `publish` runs, visitors keep seeing the previous pages. The one exception
|
|
|
77
107
|
the very first version of a project: it publishes automatically, since there is no
|
|
78
108
|
live version to protect yet.
|
|
79
109
|
|
|
110
|
+
`xflow status` says how the local copy differs from the server revision. When a deploy is
|
|
111
|
+
rejected before the build starts, the server revision is newer: somebody deployed first.
|
|
112
|
+
Fetch their work next to yours (`xflow pull --into ./server-copy`), merge it in git, deploy
|
|
113
|
+
again. `--force` destroys their work, a last resort rather than a way around the error.
|
|
114
|
+
|
|
80
115
|
**Only pages are versioned.** Cloud functions and the database are one per project: they
|
|
81
116
|
are not versioned, and dev and live share them. So `xflow deploy` changes the running
|
|
82
117
|
application the moment it finishes, before any `publish`, and `publish` moves the pages
|
|
83
118
|
only. Tell the user this when a deploy touches `functions/`: there is no staging step for
|
|
84
119
|
server code.
|
|
85
120
|
|
|
86
|
-
Rolling back: `xflow deployments` lists the
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
deliberate step: it replaces the server revision, needs `--force`, and asks a human to type
|
|
92
|
-
the project name, so you cannot do it on your own.
|
|
121
|
+
Rolling back: `xflow deployments` lists the history, `xflow rollback <id>` points the project
|
|
122
|
+
at the pages of an earlier build. Only the pages come back, and they then talk to today's
|
|
123
|
+
functions. The code of that build is a separate fetch (`xflow pull --revision N --into
|
|
124
|
+
../old-version`, kept outside the project folder); building it over the current one needs
|
|
125
|
+
`--force` and a human typing the project name, so you cannot do it on your own.
|
|
93
126
|
|
|
94
|
-
The platform keeps the **last 30 successful builds
|
|
95
|
-
|
|
96
|
-
backup
|
|
97
|
-
version was built from is never deleted), and a build is made from a revision again.
|
|
98
|
-
Keep anything you must not lose in your own git repository, not in the version history.
|
|
127
|
+
The platform keeps the **last 30 successful builds** and the **last 100 revisions** (a
|
|
128
|
+
revision a live version was built from is never deleted). That is rollback depth, not a
|
|
129
|
+
backup: keep anything you must not lose in your own git repository.
|
|
99
130
|
|
|
100
131
|
**The only link you give a person is the project page**, `https://app.getxflow.com/projects/<id>`,
|
|
101
132
|
which the CLI prints for you. Refer to builds by their number ("version 481203 is built,
|
|
@@ -105,25 +136,25 @@ and after the next publish it does not break, it keeps answering with the old co
|
|
|
105
136
|
holding that link then stares at a frozen app and concludes the changes never shipped. The
|
|
106
137
|
project page always shows the current state, and every version is reachable from it.
|
|
107
138
|
|
|
108
|
-
The platform builds the project itself, in a clean sandbox with one Node version for
|
|
109
|
-
everyone, and serves the result as static files. Nothing is built on your machine for
|
|
110
|
-
deployment, so a local `npm run build` is only a fast way to see errors early.
|
|
111
|
-
|
|
112
139
|
## Build gate
|
|
113
140
|
|
|
114
141
|
Before the sandbox starts, the platform checks the sources against the template. Every
|
|
115
|
-
rule below blocks the build, and
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
rejections.
|
|
142
|
+
rule below blocks the build, and violations come back together with files and line
|
|
143
|
+
numbers rather than one per attempt: up to fifty of them, and a count when there are
|
|
144
|
+
more. Nothing is charged for a rejected attempt, the sandbox never starts. Write code
|
|
145
|
+
that already satisfies these rules instead of learning them from rejections.
|
|
119
146
|
|
|
120
147
|
Build setup:
|
|
121
148
|
|
|
122
149
|
- `package.json` with the build script named in `xflow.json` (`npm run build` by default).
|
|
123
150
|
- Vite: a `vite.config.*` and `vite` in dependencies.
|
|
124
151
|
- No server frameworks: `next`, `nuxt`, `remix`, `@sveltejs/kit`, `astro`.
|
|
125
|
-
- `index.html` in the root
|
|
152
|
+
- `index.html` in the root and an entry point at `src/main.tsx` (`.ts`, `.jsx` and `.js`
|
|
153
|
+
count too).
|
|
126
154
|
- Application code under `src/`. Root `app/`, `pages/`, `next/` are rejected.
|
|
155
|
+
- Heavy media does not belong in the sources at all: the archive is capped at 10 MB and
|
|
156
|
+
the built application at 15 MB unpacked. Upload it with `xflow storage push` instead
|
|
157
|
+
and refer to the addresses it prints, see **File storage**.
|
|
127
158
|
|
|
128
159
|
Platform contract, checked across all of `src/`:
|
|
129
160
|
|
|
@@ -174,10 +205,15 @@ A rejection names the file and the revision to take the original from:
|
|
|
174
205
|
|
|
175
206
|
## Cloud functions
|
|
176
207
|
|
|
177
|
-
Server-side code lives in `functions/<name>/index.ts` and exports `handler
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
`
|
|
208
|
+
Server-side code lives in `functions/<name>/index.ts` and exports `handler` (`index.js` and
|
|
209
|
+
`index.mjs` are read too). The directory name is the name of the function, and the build gate
|
|
210
|
+
is strict about it: lowercase latin letters, digits and hyphens, starting with a letter or a
|
|
211
|
+
digit, up to 41 characters. `send-mail` passes, `sendMail` and `send_mail` do not, and the
|
|
212
|
+
build stops before the sandbox rather than halfway through it.
|
|
213
|
+
|
|
214
|
+
There is no separate deploy command: `xflow deploy` ships the functions and then builds the
|
|
215
|
+
application, in that order. List what is live with `xflow functions list`. The handler
|
|
216
|
+
returns `{ statusCode, body }` where `body` is a JSON string.
|
|
181
217
|
|
|
182
218
|
Keep one shape inside that string across the whole project: `{ success: true, data }`
|
|
183
219
|
when it worked, `{ success: false, error: { message, code } }` when it did not. Nothing
|
|
@@ -252,24 +288,29 @@ An outside service (a webhook from a payment provider, a bot, a CRM) has no pers
|
|
|
252
288
|
and needs a key of that one function. Keys are not issued by default and the CLI cannot
|
|
253
289
|
create one: a human issues it in the project settings: «Облачные функции» → the function →
|
|
254
290
|
«Настройки». Ask the user to do that and to paste the address back to you — never invent
|
|
255
|
-
another way in. A function holds at most two keys,
|
|
256
|
-
first without downtime, not to serve a second
|
|
291
|
+
another way in. Send it in the `X-Function-Key` header. A function holds at most two keys,
|
|
292
|
+
and the second one exists to replace the first without downtime, not to serve a second
|
|
293
|
+
consumer.
|
|
257
294
|
|
|
258
295
|
`xflow functions list` shows who can reach each function: `in-app only` (no keys, answers
|
|
259
296
|
only inside the application) or `external (N keys)` (a human issued external access). Key
|
|
260
297
|
values are never shown there.
|
|
261
298
|
|
|
299
|
+
## Secrets and connected accounts
|
|
300
|
+
|
|
262
301
|
Keys and passwords live on the platform, not in the repository: `xflow env set SMTP_PASSWORD=…`
|
|
263
302
|
writes one, `xflow env` lists the names, `xflow env check` tells you which variables your
|
|
264
303
|
functions read but the platform does not have. Values never come back out — the only place
|
|
265
304
|
they exist is inside the running function.
|
|
266
305
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
306
|
+
These commands see only what this project can see: variables shared across the organization
|
|
307
|
+
and the ones bound to this project. A variable bound to another project is invisible here, so
|
|
308
|
+
`env rm` reports it as missing even though names are unique within the organization.
|
|
309
|
+
|
|
310
|
+
Why hard rules 1 and 2 are hard rules: a function receives only the variables it mentions by
|
|
311
|
+
name, and the platform reads those names out of the text of the code, so an opaque read ships
|
|
312
|
+
a function whose secret silently never arrives. The build stops on one instead. And a value
|
|
313
|
+
written today reaches the running function on the next `xflow deploy`, not before.
|
|
273
314
|
|
|
274
315
|
Some variables come from a connected account instead of from you. When someone connects an
|
|
275
316
|
advertising cabinet or another external service in the platform settings and links it to the
|
|
@@ -286,23 +327,22 @@ alias and the state of the access, and the ones the organization has but this pr
|
|
|
286
327
|
not use yet, marked `available, not linked`. Check it before telling anyone a service is
|
|
287
328
|
unavailable: the account you need is often connected already, one link away.
|
|
288
329
|
|
|
289
|
-
`xflow connections link "Яндекс Метрика" --as YANDEX_METRIKA` is that link
|
|
290
|
-
`xflow connections unlink YANDEX_METRIKA` undoes it
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
330
|
+
`xflow connections link "Яндекс Метрика" --as YANDEX_METRIKA` is that link and
|
|
331
|
+
`xflow connections unlink YANDEX_METRIKA` undoes it; `xflow help connections` has the
|
|
332
|
+
naming rules and the flags. Unlink refuses while a function still reads one of the
|
|
333
|
+
variables and names those functions, so read that list before reaching for `--force`.
|
|
334
|
+
|
|
335
|
+
Linking needs the `connections:link` right on the key, and only accounts granted to the
|
|
336
|
+
owner of the key personally can be linked at all. If the right was taken away, say so and
|
|
337
|
+
ask the person to turn it back on in the platform settings under Developers: a key cannot
|
|
338
|
+
grant it to itself. Connecting a new account and switching one off stay with a person too.
|
|
296
339
|
|
|
297
|
-
|
|
298
|
-
away, say so and ask the person to turn it back on in the platform settings under
|
|
299
|
-
Developers: a key cannot grant it to itself. Only accounts granted to the owner of the key
|
|
300
|
-
personally can be linked at all. Connecting a new account and switching one off stay with a
|
|
301
|
-
person too.
|
|
340
|
+
## Schedules
|
|
302
341
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
342
|
+
`xflow schedules set report "0 3 ? * * *"` runs a function daily at 03:00. Six fields, UTC,
|
|
343
|
+
and exactly one of day-of-month / day-of-week must be `?` — that is how Yandex wants it.
|
|
344
|
+
A scheduled run reaches the handler as a POST with no headers, and `--payload '{"mode":"full"}'`
|
|
345
|
+
is how it gets a body. How often a schedule may run is a plan limit, see **Plan limits**.
|
|
306
346
|
|
|
307
347
|
The pieces line up in one pass. From a new function to a verified schedule:
|
|
308
348
|
|
|
@@ -310,7 +350,7 @@ The pieces line up in one pass. From a new function to a verified schedule:
|
|
|
310
350
|
xflow env set SMTP_PASSWORD=... # secrets first: values ride the next deploy
|
|
311
351
|
# write functions/report/index.ts, reading process.env.SMTP_PASSWORD literally
|
|
312
352
|
xflow deploy # ships the function, then builds the app
|
|
313
|
-
xflow schedules set report "0 3 ? * * *"
|
|
353
|
+
xflow schedules set report "0 3 ? * * *" --payload '{"mode":"full"}' # needs a deployed function
|
|
314
354
|
xflow functions invoke report # run it once, the way the app would
|
|
315
355
|
xflow functions logs report # empty output means it never crashed
|
|
316
356
|
```
|
|
@@ -336,41 +376,55 @@ You never write that variable yourself: `xflow env set DATABASE_URL=...` is refu
|
|
|
336
376
|
`xflow env rm DATABASE_URL`. The same goes for every name starting with `XFLOW`: the platform
|
|
337
377
|
fills those in itself, and your value under one of them would shadow the real one.
|
|
338
378
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
the right to destroy data. That right is off by default and only its owner can turn it on,
|
|
347
|
-
in the platform settings, under Developers. So when a destructive migration is refused for
|
|
348
|
-
the right rather than the flag, adding the flag changes nothing: say what needs deleting and
|
|
349
|
-
why, and let the person decide. With both in place the affected tables are dumped first and
|
|
350
|
-
kept for 7 days. Check with `--dry-run` before applying.
|
|
379
|
+
The platform keeps no database history and no backups, so anything that destroys data
|
|
380
|
+
(`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE FROM` without a condition) needs two
|
|
381
|
+
things at once: the `--allow-destructive` flag, and the right to destroy data on the key.
|
|
382
|
+
That right is off by default and only its owner turns it on, under Developers in the
|
|
383
|
+
platform settings. When the refusal is about the right rather than the flag, adding the flag
|
|
384
|
+
changes nothing: say what needs deleting and why, and let the person decide. With both in
|
|
385
|
+
place the affected tables are dumped first and kept for 7 days. Check with `--dry-run` first.
|
|
351
386
|
|
|
352
387
|
One logical database can be shared by several projects, so your migration can break an app
|
|
353
|
-
you do not see
|
|
354
|
-
repository: that is
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
the columns of one table. `xflow db query "select ..."` reads data, inside a READ ONLY
|
|
359
|
-
transaction, so a write there fails by design rather than by accident. Look before you write
|
|
360
|
-
a migration against a shared database.
|
|
388
|
+
you do not see, and `xflow db status` lists applied migrations that have no file in your
|
|
389
|
+
repository: that is somebody else's project. For the same reason `migrations/` is not the
|
|
390
|
+
schema. It says what you did; `xflow db schema [table]` says what is in the database right
|
|
391
|
+
now, and `xflow db query "select ..."` reads it inside a READ ONLY transaction. Look before
|
|
392
|
+
you write a migration against a shared database.
|
|
361
393
|
|
|
362
394
|
## File storage
|
|
363
395
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
396
|
+
Two kinds of files live here, and they arrive through different doors.
|
|
397
|
+
|
|
398
|
+
**Static assets of the application you are building** — product photos, video, PDFs, big
|
|
399
|
+
artwork — you upload yourself, from the terminal:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
xflow storage push ./media --to media # a folder, its structure kept
|
|
403
|
+
xflow storage ls # what is there, with addresses and sizes
|
|
404
|
+
xflow storage ls --json # the same list, machine-readable
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The caps that send them here (10 MB of sources, 15 MB unpacked) are about heavy media, not
|
|
408
|
+
about code: icons, fonts and small artwork the build needs stay in the repository as usual.
|
|
409
|
+
Everything heavy goes to storage, where it is metered against the organization plan and is
|
|
410
|
+
not re-uploaded on every deploy.
|
|
369
411
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
412
|
+
`push` prints the address of every file and skips what is already stored under the same path
|
|
413
|
+
and size, so a repeat run after a broken connection is cheap; `--replace` overwrites a file
|
|
414
|
+
whose size differs. An address belongs to the record rather than to the bytes, so a
|
|
415
|
+
replacement keeps it and the links in your code and tables keep working. Put those addresses
|
|
416
|
+
into the code or into a table: there is no command that fetches files back to a machine.
|
|
417
|
+
|
|
418
|
+
Deleting needs a right of its own, off by default (**Delete files** in the Developers
|
|
419
|
+
section of the platform), so `xflow storage rm` may answer that the key was not granted it.
|
|
420
|
+
Ask the person to switch it on, and do not look for a way around: whatever the users of the
|
|
421
|
+
application uploaded lives in the same folders, and there is no undo. A folder first tells
|
|
422
|
+
you how many files it holds and deletes them only with `--yes`.
|
|
423
|
+
|
|
424
|
+
**Files that users of the application upload** go through a cloud function of your own. The
|
|
425
|
+
browser cannot reach storage directly: those endpoints take only the server key of the
|
|
426
|
+
project, and the platform puts it into the environment of your cloud functions as
|
|
427
|
+
`XFLOW_SERVER_KEY`. Nothing else holds it: not the bundle, not `.env`, not `xflow env`.
|
|
374
428
|
|
|
375
429
|
So uploading is a function of your own. It asks the platform for a one-time link, the browser
|
|
376
430
|
then sends the bytes straight to storage, and a second call records the file:
|
|
@@ -386,35 +440,30 @@ const link = await fetch(`${process.env.XFLOW_API_URL}/api/storage/project/uploa
|
|
|
386
440
|
}).then((r) => r.json())
|
|
387
441
|
```
|
|
388
442
|
|
|
389
|
-
`confirm` takes the
|
|
390
|
-
|
|
443
|
+
`confirm` takes the returned `s3Key` and answers with the file and its address; `delete`
|
|
444
|
+
takes that same `url`. Never pipe the bytes through the function itself.
|
|
391
445
|
|
|
392
|
-
There is no endpoint that lists
|
|
393
|
-
only copy you
|
|
394
|
-
reads its files from there.
|
|
446
|
+
There is no endpoint that lists files back to a function, so the address that `confirm`
|
|
447
|
+
returns is the only copy this door gives you: write it into a table of your own in the same
|
|
448
|
+
call, and the application reads its files from there.
|
|
395
449
|
|
|
396
450
|
That address never expires and is safe to store, but it is not a public link. It opens only
|
|
397
451
|
for a visitor who is signed in and has access to this project, the same rule that guards the
|
|
398
452
|
application itself, so it works on your pages and does nothing in an email or on a page
|
|
399
453
|
anyone can open.
|
|
400
454
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
But storage keeps the header the browser sent and serves the file under it, while the
|
|
407
|
-
record keeps the one you named, so a card can say `image/png` about a file every browser
|
|
408
|
-
treats as HTML. Send the same string in both calls and they cannot drift.
|
|
409
|
-
- **The same name in the same folder is refused.** Names are unique per folder, so a second
|
|
410
|
-
`avatar.png` fails instead of replacing the first. Give the name a suffix of your own, or
|
|
411
|
-
delete the old file before confirming the new one.
|
|
455
|
+
Three things bite an upload that otherwise looks right:
|
|
456
|
+
|
|
457
|
+
- **A name already taken in that folder is refused before the link is issued.** Pass
|
|
458
|
+
`overwrite: true` to replace the file: the bytes change and the address stays, which is
|
|
459
|
+
what the pages of the application expect. Without it, give the name a suffix of your own.
|
|
412
460
|
- **The limits are 200 MB per file and the storage quota of the organization.** The quota is
|
|
413
461
|
checked again on `confirm`, by the real size, which means a refusal can land after the
|
|
414
462
|
bytes are already up; the platform then removes the object and your table stays clean.
|
|
415
463
|
- **Confirm only after the PUT has finished.** The platform looks the object up in storage
|
|
416
|
-
and takes its real size from there, not from what you declared, so an
|
|
417
|
-
answers that the file is not there.
|
|
464
|
+
and takes its real size and content type from there, not from what you declared, so an
|
|
465
|
+
early `confirm` answers that the file is not there. Send on the PUT the `Content-Type` you
|
|
466
|
+
named when asking for the link: storage serves the file under the header it received.
|
|
418
467
|
|
|
419
468
|
A refusal comes back as `{ error, code }`. Branch on `code` (`invalid_name`, `file_too_large`,
|
|
420
469
|
`quota_exceeded`, `not_uploaded`, `duplicate_name`, `not_found`, …) and never on the text:
|
|
@@ -425,33 +474,20 @@ browser can be edited by whoever opened it. Never write the key into the sources
|
|
|
425
474
|
send it to the frontend: the build gate stops on a key found in the application code, and a
|
|
426
475
|
key that reached a visitor lets them delete every file of the project.
|
|
427
476
|
|
|
428
|
-
##
|
|
429
|
-
|
|
430
|
-
`xflow status` shows how the local copy differs from the server revision.
|
|
431
|
-
`xflow deploy` sends sources, `xflow pull` fetches them.
|
|
477
|
+
## Organizations and keys
|
|
432
478
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
way around the error.
|
|
479
|
+
One key per organization, stored side by side rather than replacing each other:
|
|
480
|
+
`xflow org` lists them, `xflow org switch <name|id>` changes the active one, and
|
|
481
|
+
`xflow help org` has the precedence rules. Two things are worth knowing without asking.
|
|
437
482
|
|
|
438
|
-
|
|
483
|
+
Inside a project folder there is nothing to switch: commands follow the organization the
|
|
484
|
+
folder is bound to, whatever the active one is. That is what lets two projects of two
|
|
485
|
+
organizations work side by side.
|
|
439
486
|
|
|
440
|
-
A
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
then the active organization. `xflow org` lists the stored organizations with the active
|
|
445
|
-
one marked, `xflow org switch <name|id>` makes another one active without a browser, and
|
|
446
|
-
`xflow whoami` names the organization behind the current key.
|
|
447
|
-
|
|
448
|
-
Inside a project folder there is nothing to switch: commands follow the folder's own
|
|
449
|
-
organization whatever the active one is, which is what lets two projects of two
|
|
450
|
-
organizations work side by side. Project ids are unique across the platform, so a key of
|
|
451
|
-
the wrong organization can never touch another organization's project: the command fails
|
|
452
|
-
with "not found" instead. When that error names a project you know exists, check
|
|
453
|
-
`xflow org`; signing in to a missing organization is `xflow login`, and that needs a
|
|
454
|
-
person with a browser.
|
|
487
|
+
A "not found" on a project you know exists usually means the key belongs to another
|
|
488
|
+
organization, not that the project is gone: ids are unique across the platform, so a
|
|
489
|
+
wrong key can never reach somebody else's project. Check `xflow org`. Signing in to a
|
|
490
|
+
missing organization is `xflow login`, and that needs a person with a browser.
|
|
455
491
|
|
|
456
492
|
## Direct access without the terminal
|
|
457
493
|
|