@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.
- package/README.md +34 -4
- package/dist/filex-core.js +10626 -6501
- package/dist/filex-core.js.map +1 -1
- package/dist/filex-core.umd.cjs +94 -63
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/index.d.ts +1026 -7
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/FileExplorer.vue +103 -68
- package/src/components/ConnectionGuideView.vue +333 -0
- package/src/components/ConnectionsPanel.vue +912 -0
- package/src/components/NFSExportsPanel.vue +283 -0
- package/src/components/S3KeysPanel.vue +381 -0
- package/src/components/SSHKeysPanel.vue +222 -0
- package/src/components/StorageFields.vue +362 -0
- package/src/components/TokensPanel.vue +191 -0
- package/src/components/UploadProgress.vue +5 -1
- package/src/composables/useConnections.ts +271 -0
- package/src/composables/useFileApi.ts +15 -2
- package/src/composables/useNFSExports.ts +148 -0
- package/src/composables/useS3Keys.ts +175 -0
- package/src/composables/useSSHKeys.ts +119 -0
- package/src/composables/useThumbs.ts +1 -1
- package/src/composables/useTokens.ts +121 -0
- package/src/composables/useUploadChunked.ts +433 -164
- package/src/index.ts +77 -2
- package/src/lib/connectionGuides.ts +1279 -0
- package/src/lib/realtime.ts +1 -1
- package/src/lib/uploadResume.ts +157 -0
- package/src/locales/en.ts +413 -0
- package/src/locales/tr.ts +416 -0
- package/src/modals/ConvertModal.vue +1 -1
- package/src/styles/base.css +12 -12
- package/src/types/Connections.ts +122 -0
- package/src/types/ExplorerConfig.ts +23 -2
- package/src/types/NFSExports.ts +47 -0
- package/src/types/S3Keys.ts +55 -0
- package/src/types/SSHKeys.ts +54 -0
- 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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
+
}
|