@@ -23,6 +23,7 @@ import {
23
23
unlink ,
24
24
} from "node:fs/promises" ;
25
25
import { join } from "node:path" ;
26
+ import { handleCopy } from "@cocalc/sync-fs/lib/handle-api-call" ;
26
27
27
28
const { F_OK , W_OK , R_OK } = fs_constants ;
28
29
@@ -133,6 +134,8 @@ export default function init(): Router {
133
134
parseInt ( fields . dztotalchunkcount ) ,
134
135
chunkFullPath ,
135
136
dest ,
137
+ dest_dir ,
138
+ compute_server_id ,
136
139
) ;
137
140
138
141
res . send ( "received upload:\n\n" ) ;
@@ -157,6 +160,8 @@ async function handle_chunk_data(
157
160
total : number ,
158
161
chunk : string ,
159
162
dest : string ,
163
+ dest_dir : string ,
164
+ compute_server_id : number ,
160
165
) : Promise < void > {
161
166
const temp = dest + ".partial-upload" ;
162
167
if ( index === 0 ) {
@@ -170,20 +175,65 @@ async function handle_chunk_data(
170
175
}
171
176
// if it's the last chunk, move temp to actual file.
172
177
if ( index === total - 1 ) {
173
- await moveFile ( temp , dest ) ;
178
+ await moveFile ( temp , dest , dest_dir , compute_server_id ) ;
174
179
}
175
180
}
176
181
177
- async function moveFile ( src : string , dest : string ) : Promise < void > {
182
+ async function moveFile (
183
+ src : string ,
184
+ dest : string ,
185
+ dest_dir ?: string ,
186
+ compute_server_id ?: number ,
187
+ ) : Promise < void > {
178
188
try {
179
- await rename ( src , dest ) ;
180
- } catch ( _ ) {
181
- // in some cases, e.g., cocalc-docker, this happens:
182
- // "EXDEV: cross-device link not permitted"
183
- // so we just try again the slower way. This is slightly
184
- // inefficient, maybe, but more robust than trying to determine
185
- // if we are doing a cross device rename.
186
- await copyFile ( src , dest ) ;
187
- await unlink ( src ) ;
189
+ if ( compute_server_id ) {
190
+ // The final destination of this file upload is a compute server.
191
+ // We copy the temp file (src) to the compute server, then remove
192
+ // the temp file.
193
+ // TODO: it would obviously be much more efficient to upload directly
194
+ // to the compute server without going through cocalc at all. For
195
+ // various reasons that is simply impossible in general, unfortunately.
196
+ logger . debug ( "move temporary file to compute server" , {
197
+ src,
198
+ dest,
199
+ dest_dir,
200
+ compute_server_id,
201
+ } ) ;
202
+
203
+ // input to handleCopy must be relative to home directory,
204
+ // but src and dest are absolute paths got by putting HOME
205
+ // in the front of them:
206
+ const { HOME } = process . env ;
207
+ if ( ! HOME ) {
208
+ throw Error ( "HOME env var must be set" ) ;
209
+ }
210
+ await rename ( src , dest ) ;
211
+ await handleCopy ( {
212
+ event : "copy_from_project_to_compute_server" ,
213
+ compute_server_id,
214
+ paths : [ dest . slice ( HOME . length + 1 ) ] ,
215
+ dest : dest_dir ,
216
+ } ) ;
217
+ return ;
218
+ }
219
+
220
+ logger . debug ( "move temporary file to dest" , {
221
+ src,
222
+ dest,
223
+ } ) ;
224
+ try {
225
+ await rename ( src , dest ) ;
226
+ } catch ( _ ) {
227
+ // in some cases, e.g., cocalc-docker, this happens:
228
+ // "EXDEV: cross-device link not permitted"
229
+ // so we just try again the slower way. This is slightly
230
+ // inefficient, maybe, but more robust than trying to determine
231
+ // if we are doing a cross device rename.
232
+ await copyFile ( src , dest ) ;
233
+ }
234
+ } finally {
235
+ try {
236
+ await unlink ( src ) ;
237
+ } catch ( _ ) { }
188
238
}
189
239
}
0 commit comments