@arcote.tech/arc-files 0.7.27 → 0.7.28

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-files",
3
3
  "type": "module",
4
- "version": "0.7.27",
4
+ "version": "0.7.28",
5
5
  "private": false,
6
6
  "description": "Generic file upload fragment for Arc framework — S3 presigned upload + provider file_id binding",
7
7
  "main": "./src/index.ts",
@@ -10,9 +10,9 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "peerDependencies": {
13
- "@arcote.tech/arc": "^0.7.27",
14
- "@arcote.tech/arc-ds": "^0.7.27",
15
- "@arcote.tech/platform": "^0.7.27",
13
+ "@arcote.tech/arc": "^0.7.28",
14
+ "@arcote.tech/arc-ds": "^0.7.28",
15
+ "@arcote.tech/platform": "^0.7.28",
16
16
  "@aws-sdk/client-s3": "^3.658.0",
17
17
  "@aws-sdk/s3-request-presigner": "^3.658.0",
18
18
  "lucide-react": ">=0.400.0",
@@ -8,6 +8,7 @@ import {
8
8
  string,
9
9
  type ArcAggregateElement,
10
10
  type ArcId,
11
+ type ArcRawShape,
11
12
  } from "@arcote.tech/arc";
12
13
  import type { S3Bridge } from "../s3/s3-client";
13
14
  import type { FileProviderName } from "../types";
@@ -51,9 +52,20 @@ export function createFileTypeAggregate<const Data extends FileTypeData>(
51
52
  const metadataShape = data.metadata;
52
53
  const fullShape = mergeUnsafe(builtInFields, metadataShape);
53
54
 
54
- // `requestUpload` params zawiera built-in (name, mime, size) + WSZYSTKIE
55
- // custom metadata fields consumer dostarcza je przy uploadzie (np.
56
- // accountId, opcjonalnie workspaceId).
55
+ // `requestUpload` params = built-in (name, mime, size) + custom metadata,
56
+ // ale metadata OPCJONALNE. Pola scope (accountId itd.) wyprowadzane
57
+ // autorytatywnie z aktywnego tokena server-side (patrz handler niżej), więc
58
+ // klient nie musi — i nie powinien — ich wysyłać. Consumer może pominąć
59
+ // wartość pochodzącą z tokena; serwer i tak ją nadpisze wartością z tokena.
60
+ const optionalMetadata: ArcRawShape = {};
61
+ for (const [key, element] of Object.entries(metadataShape)) {
62
+ // `validate(undefined) === false` → element już jest opcjonalny
63
+ // (ArcOptional), nie owijaj ponownie (ArcOptional nie ma `.optional()`).
64
+ const alreadyOptional = (element as any).validate?.(undefined) === false;
65
+ optionalMetadata[key] = alreadyOptional
66
+ ? element
67
+ : (element as any).optional();
68
+ }
57
69
  const requestUploadParams = mergeUnsafe(
58
70
  {
59
71
  _id: fileId,
@@ -61,7 +73,7 @@ export function createFileTypeAggregate<const Data extends FileTypeData>(
61
73
  mime: string(),
62
74
  size: number(),
63
75
  },
64
- metadataShape,
76
+ optionalMetadata,
65
77
  );
66
78
 
67
79
  // `fileRequested` event payload = `requestUploadParams` + s3 fields
@@ -142,6 +154,27 @@ export function createFileTypeAggregate<const Data extends FileTypeData>(
142
154
  const s3Key = `${data.name}/${p._id}/${p.name}`;
143
155
  const uploadUrl = await s3.presignUpload(s3Key, p.name && p.mime);
144
156
 
157
+ // Pola scope (accountId itd.) bierzemy AUTORYTATYWNIE z aktywnego
158
+ // tokena — nigdy z params klienta. `protection.check(tokenPayload)`
159
+ // zwraca dokładnie mapowanie token→metadata (`{accountId: p.accountId}`),
160
+ // to samo, którego arc-core używa do restrykcji scope. Zapobiega
161
+ // podszyciu pod cudze accountId i znosi wymóg wysyłania go przez
162
+ // klienta (który przed załadowaniem `me` słał undefined → padała
163
+ // walidacja wejścia).
164
+ const auth = (ctx as any).$auth as
165
+ | { params: any; tokenName: string }
166
+ | undefined;
167
+ const derived: Record<string, any> = {};
168
+ if (auth?.tokenName) {
169
+ for (const protection of data.protections) {
170
+ if ((protection.token as any).name !== auth.tokenName) continue;
171
+ const restriction = protection.check(auth.params);
172
+ if (restriction && typeof restriction === "object") {
173
+ Object.assign(derived, restriction);
174
+ }
175
+ }
176
+ }
177
+
145
178
  const payload: Record<string, any> = {
146
179
  _id: p._id,
147
180
  name: p.name,
@@ -151,7 +184,7 @@ export function createFileTypeAggregate<const Data extends FileTypeData>(
151
184
  s3Key,
152
185
  };
153
186
  for (const key of Object.keys(metadataShape)) {
154
- payload[key] = p[key];
187
+ payload[key] = key in derived ? derived[key] : p[key];
155
188
  }
156
189
  await (ctx as any).fileRequested.emit(payload);
157
190