@uploadista/server 0.0.7 → 0.0.9
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/CHANGELOG.md +69 -0
- package/dist/auth/index.d.mts +2 -0
- package/dist/auth/index.mjs +1 -0
- package/dist/{auth-C77S4vQd.js → auth-BqArZeGK.mjs} +1 -1
- package/dist/auth-BqArZeGK.mjs.map +1 -0
- package/dist/{index-CvDNB1lJ.d.ts → index--Lny6VJP.d.mts} +1 -1
- package/dist/index--Lny6VJP.d.mts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +735 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1343 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +10 -7
- package/src/adapter/index.ts +10 -0
- package/src/adapter/types.ts +229 -0
- package/src/core/http-handlers/flow-http-handlers.ts +245 -0
- package/src/core/http-handlers/http-handlers.ts +72 -0
- package/src/core/http-handlers/upload-http-handlers.ts +168 -0
- package/src/core/index.ts +12 -0
- package/src/core/routes.ts +188 -0
- package/src/core/server.ts +327 -0
- package/src/core/types.ts +322 -0
- package/src/core/websocket-handlers/flow-websocket-handlers.ts +47 -0
- package/src/core/websocket-handlers/upload-websocket-handlers.ts +47 -0
- package/src/core/websocket-handlers/websocket-handlers.ts +151 -0
- package/src/core/websocket-routes.ts +136 -0
- package/src/index.ts +2 -0
- package/dist/auth/index.d.ts +0 -2
- package/dist/auth/index.js +0 -1
- package/dist/auth-C77S4vQd.js.map +0 -1
- package/dist/index-CvDNB1lJ.d.ts.map +0 -1
- package/dist/index.d.ts +0 -620
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { FlowServer } from "@uploadista/core/flow";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { AuthCacheService } from "../../cache";
|
|
4
|
+
import { AuthContextService } from "../../service";
|
|
5
|
+
import type {
|
|
6
|
+
CancelFlowRequest,
|
|
7
|
+
CancelFlowResponse,
|
|
8
|
+
GetFlowRequest,
|
|
9
|
+
GetFlowResponse,
|
|
10
|
+
GetJobStatusRequest,
|
|
11
|
+
GetJobStatusResponse,
|
|
12
|
+
PauseFlowRequest,
|
|
13
|
+
PauseFlowResponse,
|
|
14
|
+
ResumeFlowRequest,
|
|
15
|
+
ResumeFlowResponse,
|
|
16
|
+
RunFlowRequest,
|
|
17
|
+
RunFlowResponse,
|
|
18
|
+
} from "../routes";
|
|
19
|
+
|
|
20
|
+
export const handleGetFlow = ({ flowId }: GetFlowRequest) => {
|
|
21
|
+
return Effect.gen(function* () {
|
|
22
|
+
const flowServer = yield* FlowServer;
|
|
23
|
+
// Access auth context if available
|
|
24
|
+
const authService = yield* AuthContextService;
|
|
25
|
+
const clientId = yield* authService.getClientId();
|
|
26
|
+
|
|
27
|
+
if (clientId) {
|
|
28
|
+
yield* Effect.logInfo(`[Flow] Getting flow data: ${flowId}, client: ${clientId}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const flowData = yield* flowServer.getFlowData(flowId, clientId);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
status: 200,
|
|
35
|
+
body: flowData,
|
|
36
|
+
} as GetFlowResponse;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const handleRunFlow = <TRequirements>({
|
|
41
|
+
flowId,
|
|
42
|
+
storageId,
|
|
43
|
+
inputs,
|
|
44
|
+
}: RunFlowRequest) => {
|
|
45
|
+
return Effect.gen(function* () {
|
|
46
|
+
const flowServer = yield* FlowServer;
|
|
47
|
+
// Access auth context if available
|
|
48
|
+
const authService = yield* AuthContextService;
|
|
49
|
+
const authCache = yield* AuthCacheService;
|
|
50
|
+
const clientId = yield* authService.getClientId();
|
|
51
|
+
|
|
52
|
+
if (clientId) {
|
|
53
|
+
yield* Effect.logInfo(
|
|
54
|
+
`[Flow] Executing flow: ${flowId}, storage: ${storageId}, client: ${clientId}`,
|
|
55
|
+
);
|
|
56
|
+
yield* Effect.logInfo(JSON.stringify(inputs, null, 2));
|
|
57
|
+
} else {
|
|
58
|
+
yield* Effect.logInfo(`[Flow] Executing flow: ${flowId}, storage: ${storageId}`);
|
|
59
|
+
yield* Effect.logInfo(`[Flow] Inputs: ${JSON.stringify(inputs, null, 2)}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Run flow returns immediately with jobId
|
|
63
|
+
yield* Effect.logInfo(`[Flow] Calling flowServer.runFlow...`);
|
|
64
|
+
const result = yield* flowServer.runFlow<TRequirements>({
|
|
65
|
+
flowId,
|
|
66
|
+
storageId,
|
|
67
|
+
clientId,
|
|
68
|
+
inputs,
|
|
69
|
+
}).pipe(
|
|
70
|
+
Effect.tap(() => Effect.logInfo(`[Flow] runFlow completed successfully`)),
|
|
71
|
+
Effect.tapError((error) =>
|
|
72
|
+
Effect.logError(`[Flow] runFlow failed with error: ${error}`)
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Cache auth context for subsequent flow operations (continue, status)
|
|
77
|
+
const authContext = yield* authService.getAuthContext();
|
|
78
|
+
if (authContext) {
|
|
79
|
+
yield* authCache.set(result.id, authContext);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
yield* Effect.logInfo(`[Flow] Flow started with jobId: ${result.id}`);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
status: 200,
|
|
86
|
+
body: result,
|
|
87
|
+
} as RunFlowResponse;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const handleJobStatus = ({ jobId }: GetJobStatusRequest) => {
|
|
92
|
+
return Effect.gen(function* () {
|
|
93
|
+
const flowServer = yield* FlowServer;
|
|
94
|
+
// Access auth context if available
|
|
95
|
+
const authService = yield* AuthContextService;
|
|
96
|
+
const authCache = yield* AuthCacheService;
|
|
97
|
+
const clientId = yield* authService.getClientId();
|
|
98
|
+
|
|
99
|
+
if (!jobId) {
|
|
100
|
+
throw new Error("No job id");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (clientId) {
|
|
104
|
+
yield* Effect.logInfo(`[Flow] Getting job status: ${jobId}, client: ${clientId}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = yield* flowServer.getJobStatus(jobId);
|
|
108
|
+
|
|
109
|
+
// Clear cache if flow is completed or failed
|
|
110
|
+
if (result.status === "completed" || result.status === "failed") {
|
|
111
|
+
yield* authCache.delete(jobId);
|
|
112
|
+
if (clientId) {
|
|
113
|
+
yield* Effect.logInfo(
|
|
114
|
+
`[Flow] Flow ${result.status}, cleared auth cache: ${jobId}`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
status: 200,
|
|
121
|
+
body: result,
|
|
122
|
+
} as GetJobStatusResponse;
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const handleResumeFlow = <TRequirements>({
|
|
127
|
+
jobId,
|
|
128
|
+
nodeId,
|
|
129
|
+
newData,
|
|
130
|
+
}: ResumeFlowRequest) => {
|
|
131
|
+
return Effect.gen(function* () {
|
|
132
|
+
const flowServer = yield* FlowServer;
|
|
133
|
+
// Try to get auth from current request or cached auth
|
|
134
|
+
const authService = yield* AuthContextService;
|
|
135
|
+
const authCache = yield* AuthCacheService;
|
|
136
|
+
|
|
137
|
+
// Try current auth first, fallback to cached auth
|
|
138
|
+
let clientId = yield* authService.getClientId();
|
|
139
|
+
if (!clientId) {
|
|
140
|
+
const cachedAuth = yield* authCache.get(jobId);
|
|
141
|
+
clientId = cachedAuth?.clientId ?? null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (clientId) {
|
|
145
|
+
yield* Effect.logInfo(
|
|
146
|
+
`[Flow] Continuing flow: jobId=${jobId}, nodeId=${nodeId}, client: ${clientId}`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (newData === undefined) {
|
|
151
|
+
throw new Error("Missing newData");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = yield* flowServer.resumeFlow<TRequirements>({
|
|
155
|
+
jobId,
|
|
156
|
+
nodeId,
|
|
157
|
+
newData,
|
|
158
|
+
clientId,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Clear cache if flow is completed or failed
|
|
162
|
+
if (result.status === "completed" || result.status === "failed") {
|
|
163
|
+
yield* authCache.delete(jobId);
|
|
164
|
+
if (clientId) {
|
|
165
|
+
yield* Effect.logInfo(
|
|
166
|
+
`[Flow] Flow ${result.status}, cleared auth cache: ${jobId}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
status: 200,
|
|
173
|
+
body: result,
|
|
174
|
+
} as ResumeFlowResponse;
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
export const handlePauseFlow = ({ jobId }: PauseFlowRequest) => {
|
|
178
|
+
return Effect.gen(function* () {
|
|
179
|
+
const flowServer = yield* FlowServer;
|
|
180
|
+
// Try to get auth from current request or cached auth
|
|
181
|
+
const authService = yield* AuthContextService;
|
|
182
|
+
const authCache = yield* AuthCacheService;
|
|
183
|
+
|
|
184
|
+
// Try current auth first, fallback to cached auth
|
|
185
|
+
let clientId = yield* authService.getClientId();
|
|
186
|
+
if (!clientId) {
|
|
187
|
+
const cachedAuth = yield* authCache.get(jobId);
|
|
188
|
+
clientId = cachedAuth?.clientId ?? null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (clientId) {
|
|
192
|
+
yield* Effect.logInfo(`[Flow] Pausing flow: jobId=${jobId}, client: ${clientId}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const result = yield* flowServer.pauseFlow(jobId, clientId);
|
|
196
|
+
|
|
197
|
+
if (clientId) {
|
|
198
|
+
yield* Effect.logInfo(`[Flow] Flow paused: ${jobId}, status: ${result.status}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
status: 200,
|
|
203
|
+
body: result,
|
|
204
|
+
} as PauseFlowResponse;
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const handleCancelFlow = ({ jobId }: CancelFlowRequest) => {
|
|
209
|
+
return Effect.gen(function* () {
|
|
210
|
+
const flowServer = yield* FlowServer;
|
|
211
|
+
// Try to get auth from current request or cached auth
|
|
212
|
+
const authService = yield* AuthContextService;
|
|
213
|
+
const authCache = yield* AuthCacheService;
|
|
214
|
+
|
|
215
|
+
if (!jobId) {
|
|
216
|
+
throw new Error("No job id");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Try current auth first, fallback to cached auth
|
|
220
|
+
let clientId = yield* authService.getClientId();
|
|
221
|
+
if (!clientId) {
|
|
222
|
+
const cachedAuth = yield* authCache.get(jobId);
|
|
223
|
+
clientId = cachedAuth?.clientId ?? null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (clientId) {
|
|
227
|
+
yield* Effect.logInfo(
|
|
228
|
+
`[Flow] Cancelling flow: jobId=${jobId}, client: ${clientId}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const result = yield* flowServer.cancelFlow(jobId, clientId);
|
|
233
|
+
|
|
234
|
+
// Clear cache since flow is cancelled
|
|
235
|
+
yield* authCache.delete(jobId);
|
|
236
|
+
if (clientId) {
|
|
237
|
+
yield* Effect.logInfo(`[Flow] Flow cancelled, cleared auth cache: ${jobId}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
status: 200,
|
|
242
|
+
body: result,
|
|
243
|
+
} as CancelFlowResponse;
|
|
244
|
+
});
|
|
245
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { UploadistaRequest, UploadistaResponse } from "../routes";
|
|
3
|
+
import {
|
|
4
|
+
handleCancelFlow,
|
|
5
|
+
handleGetFlow,
|
|
6
|
+
handleJobStatus,
|
|
7
|
+
handlePauseFlow,
|
|
8
|
+
handleResumeFlow,
|
|
9
|
+
handleRunFlow,
|
|
10
|
+
} from "./flow-http-handlers";
|
|
11
|
+
import {
|
|
12
|
+
handleCreateUpload,
|
|
13
|
+
handleGetCapabilities,
|
|
14
|
+
handleGetUpload,
|
|
15
|
+
handleUploadChunk,
|
|
16
|
+
} from "./upload-http-handlers";
|
|
17
|
+
|
|
18
|
+
export type { UploadistaRequest, UploadistaResponse } from "../routes";
|
|
19
|
+
|
|
20
|
+
export const handleUploadistaRequest = <TRequirements>(
|
|
21
|
+
req: UploadistaRequest,
|
|
22
|
+
) => {
|
|
23
|
+
return Effect.gen(function* () {
|
|
24
|
+
switch (req.type) {
|
|
25
|
+
case "create-upload":
|
|
26
|
+
return (yield* handleCreateUpload(req)) as UploadistaResponse;
|
|
27
|
+
case "get-capabilities":
|
|
28
|
+
return (yield* handleGetCapabilities(req)) as UploadistaResponse;
|
|
29
|
+
case "get-upload":
|
|
30
|
+
return (yield* handleGetUpload(req)) as UploadistaResponse;
|
|
31
|
+
case "upload-chunk":
|
|
32
|
+
return (yield* handleUploadChunk(req)) as UploadistaResponse;
|
|
33
|
+
case "get-flow":
|
|
34
|
+
return (yield* handleGetFlow(req)) as UploadistaResponse;
|
|
35
|
+
case "run-flow":
|
|
36
|
+
return (yield* handleRunFlow<TRequirements>(req)) as UploadistaResponse;
|
|
37
|
+
case "job-status":
|
|
38
|
+
return (yield* handleJobStatus(req)) as UploadistaResponse;
|
|
39
|
+
case "resume-flow":
|
|
40
|
+
return (yield* handleResumeFlow<TRequirements>(
|
|
41
|
+
req,
|
|
42
|
+
)) as UploadistaResponse;
|
|
43
|
+
case "pause-flow":
|
|
44
|
+
return (yield* handlePauseFlow(req)) as UploadistaResponse;
|
|
45
|
+
case "cancel-flow":
|
|
46
|
+
return (yield* handleCancelFlow(req)) as UploadistaResponse;
|
|
47
|
+
case "not-found":
|
|
48
|
+
return {
|
|
49
|
+
status: 404,
|
|
50
|
+
headers: { "Content-Type": "application/json" },
|
|
51
|
+
body: { error: "Not found" },
|
|
52
|
+
} as UploadistaResponse;
|
|
53
|
+
case "bad-request":
|
|
54
|
+
return {
|
|
55
|
+
status: 400,
|
|
56
|
+
body: { error: "Bad request", message: req.message },
|
|
57
|
+
} as UploadistaResponse;
|
|
58
|
+
case "method-not-allowed":
|
|
59
|
+
return {
|
|
60
|
+
status: 405,
|
|
61
|
+
headers: { "Content-Type": "application/json" },
|
|
62
|
+
body: { error: "Method not allowed" },
|
|
63
|
+
} as UploadistaResponse;
|
|
64
|
+
case "unsupported-content-type":
|
|
65
|
+
return {
|
|
66
|
+
status: 415,
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
body: { error: "Unsupported content type" },
|
|
69
|
+
} as UploadistaResponse;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { inputFileSchema } from "@uploadista/core/types";
|
|
2
|
+
import { UploadServer } from "@uploadista/core/upload";
|
|
3
|
+
import { isSupportedAlgorithm } from "@uploadista/core/utils";
|
|
4
|
+
import { MetricsService } from "@uploadista/observability";
|
|
5
|
+
import { Effect } from "effect";
|
|
6
|
+
import { AuthCacheService } from "../../cache";
|
|
7
|
+
import { ValidationError } from "../../error-types";
|
|
8
|
+
import { AuthContextService } from "../../service";
|
|
9
|
+
import type {
|
|
10
|
+
CreateUploadRequest,
|
|
11
|
+
CreateUploadResponse,
|
|
12
|
+
GetCapabilitiesRequest,
|
|
13
|
+
GetCapabilitiesResponse,
|
|
14
|
+
GetUploadRequest,
|
|
15
|
+
GetUploadResponse,
|
|
16
|
+
UploadChunkRequest,
|
|
17
|
+
UploadChunkResponse,
|
|
18
|
+
} from "../routes";
|
|
19
|
+
|
|
20
|
+
export const handleCreateUpload = (req: CreateUploadRequest) =>
|
|
21
|
+
Effect.gen(function* () {
|
|
22
|
+
const server = yield* UploadServer;
|
|
23
|
+
// Access auth context if available
|
|
24
|
+
const authService = yield* AuthContextService;
|
|
25
|
+
const authCache = yield* AuthCacheService;
|
|
26
|
+
const clientId = yield* authService.getClientId();
|
|
27
|
+
|
|
28
|
+
if (clientId) {
|
|
29
|
+
yield* Effect.logInfo(`[Upload] Creating upload for client: ${clientId}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parsedInputFile = yield* Effect.sync(() =>
|
|
33
|
+
inputFileSchema.safeParse(req.data),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (!parsedInputFile.success) {
|
|
37
|
+
return yield* Effect.fail(
|
|
38
|
+
new ValidationError("Invalid input file schema"),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate checksum algorithm if provided
|
|
43
|
+
if (
|
|
44
|
+
parsedInputFile.data.checksumAlgorithm &&
|
|
45
|
+
!isSupportedAlgorithm(parsedInputFile.data.checksumAlgorithm)
|
|
46
|
+
) {
|
|
47
|
+
return yield* Effect.fail(
|
|
48
|
+
new ValidationError(
|
|
49
|
+
`Unsupported checksum algorithm: ${parsedInputFile.data.checksumAlgorithm}. Supported algorithms: sha256`,
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fileCreated = yield* server.createUpload(
|
|
55
|
+
parsedInputFile.data,
|
|
56
|
+
clientId,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Cache auth context for subsequent chunk uploads
|
|
60
|
+
const authContext = yield* authService.getAuthContext();
|
|
61
|
+
if (authContext) {
|
|
62
|
+
yield* authCache.set(fileCreated.id, authContext);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (clientId) {
|
|
66
|
+
yield* Effect.logInfo(
|
|
67
|
+
`[Upload] Upload created: ${fileCreated.id} for client: ${clientId}`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
status: 200,
|
|
73
|
+
body: fileCreated,
|
|
74
|
+
} as CreateUploadResponse;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const handleGetCapabilities = ({ storageId }: GetCapabilitiesRequest) =>
|
|
78
|
+
Effect.gen(function* () {
|
|
79
|
+
const server = yield* UploadServer;
|
|
80
|
+
const authService = yield* AuthContextService;
|
|
81
|
+
const clientId = yield* authService.getClientId();
|
|
82
|
+
|
|
83
|
+
const capabilities = yield* server.getCapabilities(storageId, clientId);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
status: 200,
|
|
87
|
+
body: {
|
|
88
|
+
storageId,
|
|
89
|
+
capabilities,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
},
|
|
92
|
+
} as GetCapabilitiesResponse;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const handleGetUpload = ({ uploadId }: GetUploadRequest) =>
|
|
96
|
+
Effect.gen(function* () {
|
|
97
|
+
const server = yield* UploadServer;
|
|
98
|
+
const fileResult = yield* server.getUpload(uploadId);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
status: 200,
|
|
102
|
+
body: fileResult,
|
|
103
|
+
} as GetUploadResponse;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export const handleUploadChunk = (req: UploadChunkRequest) =>
|
|
107
|
+
Effect.gen(function* () {
|
|
108
|
+
const server = yield* UploadServer;
|
|
109
|
+
// Try to get auth from current request or cached auth
|
|
110
|
+
const authService = yield* AuthContextService;
|
|
111
|
+
const authCache = yield* AuthCacheService;
|
|
112
|
+
const metricsService = yield* MetricsService;
|
|
113
|
+
|
|
114
|
+
const { uploadId, data } = req;
|
|
115
|
+
|
|
116
|
+
// Try current auth first, fallback to cached auth
|
|
117
|
+
let clientId = yield* authService.getClientId();
|
|
118
|
+
let authMetadata = yield* authService.getMetadata();
|
|
119
|
+
if (!clientId) {
|
|
120
|
+
const cachedAuth = yield* authCache.get(uploadId);
|
|
121
|
+
clientId = cachedAuth?.clientId ?? null;
|
|
122
|
+
authMetadata = cachedAuth?.metadata ?? {};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (clientId) {
|
|
126
|
+
yield* Effect.logInfo(
|
|
127
|
+
`[Upload] Uploading chunk for upload: ${uploadId}, client: ${clientId}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const fileResult = yield* server.uploadChunk(uploadId, clientId, data);
|
|
132
|
+
|
|
133
|
+
// Clear cache and record metrics if upload is complete
|
|
134
|
+
if (fileResult.size && fileResult.offset >= fileResult.size) {
|
|
135
|
+
yield* authCache.delete(uploadId);
|
|
136
|
+
if (clientId) {
|
|
137
|
+
yield* Effect.logInfo(
|
|
138
|
+
`[Upload] Upload completed, cleared auth cache: ${uploadId}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Record upload metrics if we have organization ID
|
|
143
|
+
|
|
144
|
+
if (clientId && fileResult.size) {
|
|
145
|
+
yield* Effect.logInfo(
|
|
146
|
+
`[Upload] Recording metrics for org: ${clientId}, size: ${fileResult.size}`,
|
|
147
|
+
);
|
|
148
|
+
yield* Effect.forkDaemon(
|
|
149
|
+
metricsService.recordUpload(clientId, fileResult.size, authMetadata),
|
|
150
|
+
);
|
|
151
|
+
} else {
|
|
152
|
+
yield* Effect.logWarning(
|
|
153
|
+
`[Upload] Cannot record metrics - missing organizationId or size`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (clientId) {
|
|
159
|
+
yield* Effect.logInfo(
|
|
160
|
+
`[Upload] Chunk uploaded for upload: ${uploadId}, client: ${clientId}`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
status: 200,
|
|
166
|
+
body: fileResult,
|
|
167
|
+
} as UploadChunkResponse;
|
|
168
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core server module for framework-agnostic Uploadista server implementation.
|
|
3
|
+
*
|
|
4
|
+
* This module contains the unified server implementation that works across
|
|
5
|
+
* all framework adapters (Hono, Express, Fastify, etc.).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export * from "./routes";
|
|
9
|
+
export * from "./server";
|
|
10
|
+
export * from "./types";
|
|
11
|
+
export * from "./websocket-handlers/websocket-handlers";
|
|
12
|
+
export * from "./websocket-routes";
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DataStoreCapabilities,
|
|
3
|
+
FlowData,
|
|
4
|
+
FlowJob,
|
|
5
|
+
UploadFile,
|
|
6
|
+
} from "@uploadista/core";
|
|
7
|
+
import type { StandardResponse } from "../adapter/types";
|
|
8
|
+
|
|
9
|
+
export type UploadistaRouteType =
|
|
10
|
+
| "create-upload"
|
|
11
|
+
| "get-capabilities"
|
|
12
|
+
| "get-upload"
|
|
13
|
+
| "upload-chunk"
|
|
14
|
+
| "get-flow"
|
|
15
|
+
| "run-flow"
|
|
16
|
+
| "job-status"
|
|
17
|
+
| "resume-flow"
|
|
18
|
+
| "pause-flow"
|
|
19
|
+
| "cancel-flow"
|
|
20
|
+
| "not-found"
|
|
21
|
+
| "bad-request"
|
|
22
|
+
| "method-not-allowed"
|
|
23
|
+
| "unsupported-content-type";
|
|
24
|
+
|
|
25
|
+
export type UploadistaRoute<T extends UploadistaRouteType> = {
|
|
26
|
+
type: T;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type UploadistaStandardResponse<
|
|
30
|
+
T extends UploadistaRouteType,
|
|
31
|
+
ResponseBody,
|
|
32
|
+
Status extends number = 200,
|
|
33
|
+
> = UploadistaRoute<T> & {
|
|
34
|
+
status: Status;
|
|
35
|
+
headers: { "Content-Type": "application/json" };
|
|
36
|
+
body: ResponseBody;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type NotFoundRequest = UploadistaRoute<"not-found">;
|
|
40
|
+
|
|
41
|
+
export type NotFoundResponse = UploadistaStandardResponse<
|
|
42
|
+
"not-found",
|
|
43
|
+
{ error: "Not found" },
|
|
44
|
+
404
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
export type MethodNotAllowedRequest = UploadistaRoute<"method-not-allowed">;
|
|
48
|
+
|
|
49
|
+
export type MethodNotAllowedResponse = UploadistaStandardResponse<
|
|
50
|
+
"method-not-allowed",
|
|
51
|
+
{ error: "Method not allowed" },
|
|
52
|
+
405
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
export type BadRequestRequest = UploadistaRoute<"bad-request"> & {
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type BadRequestResponse = UploadistaStandardResponse<
|
|
60
|
+
"bad-request",
|
|
61
|
+
{ error: "Bad request"; message: string },
|
|
62
|
+
400
|
|
63
|
+
>;
|
|
64
|
+
|
|
65
|
+
export type UnsupportedContentTypeRequest =
|
|
66
|
+
UploadistaRoute<"unsupported-content-type">;
|
|
67
|
+
|
|
68
|
+
export type UnsupportedContentTypeResponse = UploadistaStandardResponse<
|
|
69
|
+
"unsupported-content-type",
|
|
70
|
+
{ error: "Unsupported content type" },
|
|
71
|
+
415
|
|
72
|
+
>;
|
|
73
|
+
export type CreateUploadRequest = UploadistaRoute<"create-upload"> & {
|
|
74
|
+
data: unknown;
|
|
75
|
+
};
|
|
76
|
+
export type CreateUploadResponse = UploadistaStandardResponse<
|
|
77
|
+
"create-upload",
|
|
78
|
+
UploadFile
|
|
79
|
+
>;
|
|
80
|
+
export type GetCapabilitiesRequest = UploadistaRoute<"get-capabilities"> & {
|
|
81
|
+
storageId: string;
|
|
82
|
+
};
|
|
83
|
+
export type GetCapabilitiesResponse = UploadistaStandardResponse<
|
|
84
|
+
"get-capabilities",
|
|
85
|
+
{
|
|
86
|
+
storageId: string;
|
|
87
|
+
capabilities: DataStoreCapabilities;
|
|
88
|
+
timestamp: string;
|
|
89
|
+
}
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
export type GetUploadRequest = UploadistaRoute<"get-upload"> & {
|
|
93
|
+
uploadId: string;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export type GetUploadResponse = UploadistaStandardResponse<
|
|
97
|
+
"get-upload",
|
|
98
|
+
UploadFile
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
export type UploadChunkRequest = UploadistaRoute<"upload-chunk"> & {
|
|
102
|
+
uploadId: string;
|
|
103
|
+
data: ReadableStream;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type UploadChunkResponse = UploadistaStandardResponse<
|
|
107
|
+
"upload-chunk",
|
|
108
|
+
UploadFile
|
|
109
|
+
>;
|
|
110
|
+
|
|
111
|
+
export type GetFlowRequest = UploadistaRoute<"get-flow"> & {
|
|
112
|
+
flowId: string;
|
|
113
|
+
};
|
|
114
|
+
export type GetFlowResponse = UploadistaStandardResponse<"get-flow", FlowData>;
|
|
115
|
+
|
|
116
|
+
export type RunFlowRequest = UploadistaRoute<"run-flow"> & {
|
|
117
|
+
flowId: string;
|
|
118
|
+
storageId: string;
|
|
119
|
+
inputs: Record<string, unknown>;
|
|
120
|
+
};
|
|
121
|
+
export type RunFlowResponse = UploadistaStandardResponse<"run-flow", FlowJob>;
|
|
122
|
+
|
|
123
|
+
export type GetJobStatusRequest = UploadistaRoute<"job-status"> & {
|
|
124
|
+
jobId: string;
|
|
125
|
+
};
|
|
126
|
+
export type GetJobStatusResponse = UploadistaStandardResponse<
|
|
127
|
+
"job-status",
|
|
128
|
+
FlowJob
|
|
129
|
+
>;
|
|
130
|
+
|
|
131
|
+
export type ResumeFlowRequest = UploadistaRoute<"resume-flow"> & {
|
|
132
|
+
jobId: string;
|
|
133
|
+
nodeId: string;
|
|
134
|
+
newData: unknown;
|
|
135
|
+
};
|
|
136
|
+
export type ResumeFlowResponse = UploadistaStandardResponse<
|
|
137
|
+
"resume-flow",
|
|
138
|
+
FlowJob
|
|
139
|
+
>;
|
|
140
|
+
|
|
141
|
+
export type PauseFlowRequest = UploadistaRoute<"pause-flow"> & {
|
|
142
|
+
jobId: string;
|
|
143
|
+
};
|
|
144
|
+
export type PauseFlowResponse = UploadistaStandardResponse<
|
|
145
|
+
"pause-flow",
|
|
146
|
+
FlowJob
|
|
147
|
+
>;
|
|
148
|
+
|
|
149
|
+
export type CancelFlowRequest = UploadistaRoute<"cancel-flow"> & {
|
|
150
|
+
jobId: string;
|
|
151
|
+
};
|
|
152
|
+
export type CancelFlowResponse = UploadistaStandardResponse<
|
|
153
|
+
"cancel-flow",
|
|
154
|
+
FlowJob
|
|
155
|
+
>;
|
|
156
|
+
|
|
157
|
+
export type UploadistaRequest =
|
|
158
|
+
| CreateUploadRequest
|
|
159
|
+
| GetCapabilitiesRequest
|
|
160
|
+
| GetUploadRequest
|
|
161
|
+
| UploadChunkRequest
|
|
162
|
+
| GetFlowRequest
|
|
163
|
+
| RunFlowRequest
|
|
164
|
+
| GetJobStatusRequest
|
|
165
|
+
| ResumeFlowRequest
|
|
166
|
+
| PauseFlowRequest
|
|
167
|
+
| CancelFlowRequest
|
|
168
|
+
| NotFoundRequest
|
|
169
|
+
| BadRequestRequest
|
|
170
|
+
| MethodNotAllowedRequest
|
|
171
|
+
| UnsupportedContentTypeRequest;
|
|
172
|
+
|
|
173
|
+
export type UploadistaResponse =
|
|
174
|
+
| CreateUploadResponse
|
|
175
|
+
| GetCapabilitiesResponse
|
|
176
|
+
| GetUploadResponse
|
|
177
|
+
| UploadChunkResponse
|
|
178
|
+
| GetFlowResponse
|
|
179
|
+
| RunFlowResponse
|
|
180
|
+
| GetJobStatusResponse
|
|
181
|
+
| ResumeFlowResponse
|
|
182
|
+
| PauseFlowResponse
|
|
183
|
+
| CancelFlowResponse
|
|
184
|
+
| NotFoundResponse
|
|
185
|
+
| BadRequestResponse
|
|
186
|
+
| MethodNotAllowedResponse
|
|
187
|
+
| UnsupportedContentTypeResponse
|
|
188
|
+
| StandardResponse;
|