@brftech/filex-core 0.19.0 → 0.20.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.
Files changed (39) hide show
  1. package/README.md +34 -4
  2. package/dist/filex-core.js +10626 -6501
  3. package/dist/filex-core.js.map +1 -1
  4. package/dist/filex-core.umd.cjs +94 -63
  5. package/dist/filex-core.umd.cjs.map +1 -1
  6. package/dist/index.d.ts +1026 -7
  7. package/dist/style.css +1 -1
  8. package/package.json +3 -3
  9. package/src/FileExplorer.vue +103 -68
  10. package/src/components/ConnectionGuideView.vue +333 -0
  11. package/src/components/ConnectionsPanel.vue +912 -0
  12. package/src/components/NFSExportsPanel.vue +283 -0
  13. package/src/components/S3KeysPanel.vue +381 -0
  14. package/src/components/SSHKeysPanel.vue +222 -0
  15. package/src/components/StorageFields.vue +362 -0
  16. package/src/components/TokensPanel.vue +191 -0
  17. package/src/components/UploadProgress.vue +5 -1
  18. package/src/composables/useConnections.ts +271 -0
  19. package/src/composables/useFileApi.ts +15 -2
  20. package/src/composables/useNFSExports.ts +148 -0
  21. package/src/composables/useS3Keys.ts +175 -0
  22. package/src/composables/useSSHKeys.ts +119 -0
  23. package/src/composables/useThumbs.ts +1 -1
  24. package/src/composables/useTokens.ts +121 -0
  25. package/src/composables/useUploadChunked.ts +433 -164
  26. package/src/index.ts +77 -2
  27. package/src/lib/connectionGuides.ts +1279 -0
  28. package/src/lib/realtime.ts +1 -1
  29. package/src/lib/uploadResume.ts +157 -0
  30. package/src/locales/en.ts +413 -0
  31. package/src/locales/tr.ts +416 -0
  32. package/src/modals/ConvertModal.vue +1 -1
  33. package/src/styles/base.css +12 -12
  34. package/src/types/Connections.ts +122 -0
  35. package/src/types/ExplorerConfig.ts +23 -2
  36. package/src/types/NFSExports.ts +47 -0
  37. package/src/types/S3Keys.ts +55 -0
  38. package/src/types/SSHKeys.ts +54 -0
  39. package/src/types/Tokens.ts +39 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Connections — the shapes the storage-connection surface works with.
3
+ *
4
+ * Every one of these mirrors something the BACKEND declares, on purpose.
5
+ * `StorageField` / `StorageDriverDescriptor` are the wire form of
6
+ * `backend/internal/storage/descriptor.go`, served by
7
+ * `GET /api/admin/storage-drivers`: a driver states its own config keys,
8
+ * their types, which one is the storage root and which hold credentials,
9
+ * and every surface renders that one declaration.
10
+ *
11
+ * The alternative — a form per surface — is exactly what shipped broken:
12
+ * three of the four drivers the admin UI offered could not be created
13
+ * through it because the form collected keys the backend never read. A
14
+ * second hand-written form in the desktop app would have been a fourth
15
+ * copy of the same mistake.
16
+ */
17
+
18
+ export type StorageFieldType = 'string' | 'int' | 'bool' | 'password' | 'select';
19
+
20
+ export interface StorageFieldOption {
21
+ value: string;
22
+ /** English fallback; `i18n_key` wins when the catalogue has it. */
23
+ label: string;
24
+ i18n_key?: string;
25
+ }
26
+
27
+ /** One config key of a storage driver, as declared by the driver itself. */
28
+ export interface StorageField {
29
+ key: string;
30
+ type: StorageFieldType;
31
+ /** English fallback label — used only when `i18n_key` is missing from
32
+ * the catalogue, so a driver released after this build still renders
33
+ * readable labels instead of raw keys. */
34
+ label: string;
35
+ help?: string;
36
+ i18n_key: string;
37
+ help_i18n_key?: string;
38
+ required: boolean;
39
+ /** Credential material: render masked, never log, never put in a URL. */
40
+ secret: boolean;
41
+ default?: unknown;
42
+ placeholder?: string;
43
+ options?: StorageFieldOption[];
44
+ min?: number;
45
+ max?: number;
46
+ monospace?: boolean;
47
+ multiline?: boolean;
48
+ advanced?: boolean;
49
+ /** THE field that scopes the storage inside the backend (s3 prefix,
50
+ * local path, sftp/ftp/webdav root). The backend rejects an empty or
51
+ * "/" value with ROOT_PATH_FORBIDDEN. */
52
+ root?: boolean;
53
+ /** Legacy spellings the driver still reads for this field. */
54
+ aliases?: string[];
55
+ }
56
+
57
+ export interface StorageDriverCapabilities {
58
+ read?: boolean;
59
+ write?: boolean;
60
+ move?: boolean;
61
+ copy?: boolean;
62
+ delete?: boolean;
63
+ mkdir?: boolean;
64
+ presign?: boolean;
65
+ watch?: boolean;
66
+ }
67
+
68
+ export interface StorageDriverDescriptor {
69
+ driver: string;
70
+ label: string;
71
+ i18n_key: string;
72
+ fields: StorageField[];
73
+ capabilities: StorageDriverCapabilities;
74
+ }
75
+
76
+ /** A configured storage, as `GET /api/admin/storages` returns it. */
77
+ export interface StorageRow {
78
+ id: number;
79
+ name: string;
80
+ driver: string;
81
+ enabled: boolean;
82
+ read_only: boolean;
83
+ config: Record<string, unknown>;
84
+ rbac_enabled?: boolean;
85
+ sync_mode?: string;
86
+ created_at?: string;
87
+ updated_at?: string;
88
+ stats?: { file_count?: number; total_size_bytes?: number };
89
+ }
90
+
91
+ /** POST/PATCH body for a storage. */
92
+ export interface StorageWrite {
93
+ name: string;
94
+ driver: string;
95
+ config: Record<string, unknown>;
96
+ read_only?: boolean;
97
+ enabled?: boolean;
98
+ }
99
+
100
+ export interface StorageTestResult {
101
+ ok: boolean;
102
+ error?: string;
103
+ object_count?: number;
104
+ sample_listing?: unknown[];
105
+ }
106
+
107
+ /** The signed-in account, from `GET /api/auth/me`. */
108
+ export interface ConnectionsUser {
109
+ id: number;
110
+ email: string;
111
+ display_name?: string;
112
+ role?: string;
113
+ }
114
+
115
+ /**
116
+ * Why the caller cannot manage storages, when they cannot.
117
+ *
118
+ * `none` is the honest answer for a signed-in non-admin: the surface then
119
+ * shows the connection guides (which every user needs) and a plain "ask
120
+ * your administrator" line instead of a form that would 403 on submit.
121
+ */
122
+ export type ManageDenial = 'none' | 'anonymous' | 'unreachable';
@@ -47,6 +47,15 @@ export type LocaleCode = 'tr' | 'en';
47
47
  */
48
48
  export interface EndpointMap {
49
49
  manager: string;
50
+ /**
51
+ * Staged upload — the chunked, resumable, driver-agnostic path every client
52
+ * speaks (docs/UPLOADS.md). The per-upload routes (`PUT/GET/DELETE {id}`,
53
+ * `POST {id}/commit`) are derived from it by stripping `/begin`, so one
54
+ * override moves the whole protocol.
55
+ */
56
+ uploadBegin: string | null;
57
+ /** Legacy S3-presigned chunked upload. Still served by the backend for
58
+ * older embedders; nothing in this package calls it. */
50
59
  uploadInit: string | null;
51
60
  uploadFinalize: string | null;
52
61
  uploadAbort: string | null;
@@ -83,6 +92,8 @@ export interface ExplorerConfig {
83
92
  endpoint?: string;
84
93
 
85
94
  // ——— Per-route overrides (optional; auto-derived from apiBase if absent) ———
95
+ /** Staged upload entry point; the `{id}` routes hang off it. */
96
+ uploadBegin?: string;
86
97
  uploadInit?: string;
87
98
  uploadFinalize?: string;
88
99
  uploadAbort?: string;
@@ -198,10 +209,20 @@ export interface ExplorerConfig {
198
209
  */
199
210
  viewerBaseUrl?: string;
200
211
 
201
- /** Upload chunk size (bytes). Default 5 MB. */
212
+ /**
213
+ * Upload chunk size (bytes). Default 8 MB — the server's default too, so
214
+ * "large enough to chunk" means the same thing on both ends. The value the
215
+ * server returns from `begin` is binding; this is what the client asks for
216
+ * and the threshold above which a file goes on the staged path at all.
217
+ */
202
218
  chunkSize?: number;
203
219
 
204
- /** Parallel chunks. Default 4. */
220
+ /**
221
+ * @deprecated Ignored since the move to the staged protocol. Chunks are sent
222
+ * sequentially because `offset` — the resume point — is the contiguous run
223
+ * from part 1; parallel parts would leave holes that a resumed upload has to
224
+ * re-send anyway.
225
+ */
205
226
  parallelChunks?: number;
206
227
 
207
228
  /** Theme. */
@@ -0,0 +1,47 @@
1
+ /**
2
+ * NFS exports, as the frontend sees them.
3
+ *
4
+ * ⚠⚠ The `path` a freshly minted export returns IS the credential. NFSv3 cannot
5
+ * authenticate a request without Kerberos, so filex binds the identity to the
6
+ * export path instead: 32 bytes of entropy, shown once, stored hashed. Whoever
7
+ * knows it can mount as that account — which is why it belongs in a config file
8
+ * treated like a password, and why the UI has to say so rather than letting
9
+ * somebody discover it from a mount table.
10
+ */
11
+
12
+ export interface NFSExport {
13
+ id: number;
14
+ user_id: number;
15
+ api_token_id?: number | null;
16
+ label: string;
17
+ /** Confinement, in the same shape the S3 keys use. */
18
+ storage_name?: string;
19
+ prefix?: string;
20
+ /** Refuses every write through this mount, whatever the account may do. */
21
+ read_only: boolean;
22
+ /** Comma-separated CIDR allow-list. Empty = any address the listener takes. */
23
+ allow_cidrs?: string;
24
+ created_at: string;
25
+ last_used_at?: string | null;
26
+ expires_at?: string | null;
27
+ disabled_at?: string | null;
28
+ }
29
+
30
+ /** Where an NFS client should point, reported by the server. */
31
+ export interface NFSConnection {
32
+ /** The FILEX_NFS kill switch. */
33
+ enabled: boolean;
34
+ host: string;
35
+ /**
36
+ * ⚠ 2049 by convention, but filex serves mount AND nfs on this one port and
37
+ * runs no portmapper — so a client needs `port=` and `mountport=` both set to
38
+ * it. A guide that omitted either produces a mount that hangs.
39
+ */
40
+ port: number;
41
+ }
42
+
43
+ /** What minting returns. The path appears exactly once. */
44
+ export interface NFSExportCreated extends NFSConnection {
45
+ export: NFSExport;
46
+ path: string;
47
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The S3 access-key surface, as the frontend sees it.
3
+ *
4
+ * Mirrors `model.S3AccessKey` and the connection facts the key endpoints
5
+ * return alongside it. The facts travel WITH the keys on purpose: a key
6
+ * without an endpoint is not a usable credential, and a UI that assembled
7
+ * the URL itself would be a second place to get it wrong (the application
8
+ * URL and the S3 endpoint are different hosts whenever one is configured).
9
+ */
10
+
11
+ export interface S3AccessKey {
12
+ id: number;
13
+ access_key_id: string;
14
+ user_id: number;
15
+ api_token_id?: number | null;
16
+ label: string;
17
+ /** Optional confinement, in the shape S3 clients already understand. */
18
+ bucket?: string;
19
+ prefix?: string;
20
+ created_at: string;
21
+ last_used_at?: string | null;
22
+ expires_at?: string | null;
23
+ /** Set = the key is switched off but still in the audit trail. */
24
+ disabled_at?: string | null;
25
+ }
26
+
27
+ /** Where to point a client, and how to address it. */
28
+ export interface S3Connection {
29
+ /** Absolute endpoint URL — the S3 host's root, or `<app>/s3`. */
30
+ endpoint: string;
31
+ /** The FILEX_S3 kill switch. False = the operator turned the endpoint off. */
32
+ enabled: boolean;
33
+ /**
34
+ * True when there is no dedicated host, so clients must be told to force
35
+ * path-style addressing. A current SDK defaults to virtual-hosted and then
36
+ * fails at DNS with an error that names neither filex nor the cause.
37
+ */
38
+ path_style: boolean;
39
+ }
40
+
41
+ /** What a freshly minted key returns. The secret appears exactly once. */
42
+ export interface S3KeyCreated extends S3Connection {
43
+ key: S3AccessKey;
44
+ secret: string;
45
+ }
46
+
47
+ /** What the caller asked for when minting. */
48
+ export interface S3KeyRequest {
49
+ label: string;
50
+ /** Mint FROM an API token, inheriting its scopes, confinement and expiry. */
51
+ api_token_id?: number;
52
+ bucket?: string;
53
+ prefix?: string;
54
+ expires_at?: string;
55
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The SSH key surface, as the frontend sees it.
3
+ *
4
+ * Mirrors `model.SSHPublicKey` plus the connection facts the endpoint returns
5
+ * with it — the host, the port and the LOGIN NAME, which is the piece people
6
+ * most often get wrong when they type it themselves.
7
+ */
8
+
9
+ export interface SSHPublicKey {
10
+ id: number;
11
+ user_id: number;
12
+ name: string;
13
+ /** SHA256 fingerprint, base64, without the `SHA256:` prefix. */
14
+ fingerprint: string;
15
+ /** The normalised wire form, `<type> <base64>`. */
16
+ public_key: string;
17
+ created_at: string;
18
+ last_used_at?: string | null;
19
+ /** Set = the key is switched off but still listed. */
20
+ disabled_at?: string | null;
21
+ }
22
+
23
+ /** Where an SSH client should connect, and as whom. */
24
+ export interface SSHConnection {
25
+ /** The FILEX_SFTP kill switch. False = the operator turned the port off. */
26
+ enabled: boolean;
27
+ host: string;
28
+ /**
29
+ * ⚠ Not 22 and not the web port. SFTP is raw TCP on a port of its own, and
30
+ * a guide that printed 443 would send every client at a proxy that speaks
31
+ * only HTTP.
32
+ */
33
+ port: number;
34
+ /** The login name: the account's username, or its e-mail when it has none. */
35
+ login: string;
36
+ /** The FTPS endpoint, reported by the same call. */
37
+ ftps?: FTPSFacts;
38
+ }
39
+
40
+ /** What an FTP client needs to be told, computed on the server. */
41
+ export interface FTPSFacts {
42
+ enabled: boolean;
43
+ host: string;
44
+ port: number;
45
+ /**
46
+ * ⚠ The passive data-port range. A firewall that blocks it makes every
47
+ * transfer HANG with no error on either side — the classic FTP failure, and
48
+ * impossible to diagnose from the client end. It belongs in the guide.
49
+ */
50
+ pasv_min: number;
51
+ pasv_max: number;
52
+ /** True when no certificate was configured and the server generated one. */
53
+ self_signed: boolean;
54
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The self-service API-token surface (`/api/tokens`).
3
+ *
4
+ * ⚠ Why this exists at all: three of the six protocols filex serves take an
5
+ * API token as their password — FTPS, WebDAV and `filex mount`. Until
6
+ * 2026-08-17 the only place to mint one was `/api/admin/ai-tokens`, which is
7
+ * admin-only, so a normal user opened the FTPS guide, read "use an API token
8
+ * as the password", and had nowhere to get one. The backend route has always
9
+ * been open to every account; only the UI was missing.
10
+ */
11
+
12
+ /** One token row. The secret is NEVER in here — only the hash is stored. */
13
+ export interface ApiToken {
14
+ id: number;
15
+ label: string;
16
+ scopes: string;
17
+ /** Per-token display identities for the audit trail; may be absent. */
18
+ usernames?: string | null;
19
+ created_at?: string;
20
+ last_used_at?: string | null;
21
+ expires_at?: string | null;
22
+ }
23
+
24
+ /** What comes back from a mint. `token` is shown exactly once. */
25
+ export interface ApiTokenCreated {
26
+ token: string;
27
+ row: ApiToken;
28
+ }
29
+
30
+ export interface ApiTokenRequest {
31
+ label: string;
32
+ /**
33
+ * Comma-separated verbs. The server CAPS this against the caller's role and
34
+ * their own grants, so asking for more than you have is refused rather than
35
+ * quietly granted — never send `admin`, it is rejected outright here.
36
+ */
37
+ scopes?: string;
38
+ expires_in_days?: number;
39
+ }