@@ -8,7 +8,7 @@ import { join, resolve, relative } from 'pathe'
88import { execa } from 'execa'
99import { setupDotenv } from 'c12'
1010import { $api , fetchUser , selectTeam , selectProject , projectPath , fetchProject , linkProject , gitInfo } from '../utils/index.mjs'
11- import { getStorage , getPathsToDeploy , getFile , uploadAssetsToCloudflare , isMetaPath , isServerPath , getPublicFiles } from '../utils/deploy.mjs'
11+ import { getStorage , getPathsToDeploy , getFile , uploadAssetsToCloudflare , uploadWorkersAssetsToCloudflare , isMetaPath , isWorkerMetaPath , isServerPath , isWorkerServerPath , getPublicFiles , getWorkerPublicFiles } from '../utils/deploy.mjs'
1212import { createMigrationsTable , fetchRemoteMigrations , queryDatabase } from '../utils/database.mjs'
1313import login from './login.mjs'
1414import ensure from './ensure.mjs'
@@ -105,6 +105,16 @@ export default defineCommand({
105105 consola . success ( `Connected to ${ colors . blueBright ( linkedProject . teamSlug ) } team.` )
106106 consola . success ( `Linked to ${ colors . blueBright ( linkedProject . slug ) } project.` )
107107
108+ if ( linkedProject . type === 'worker' && deployEnv === 'preview' ) {
109+ consola . warn ( 'Currently NuxtHub on Workers (BETA) does not support preview environments.' )
110+ const shouldDeploy = await confirm ( {
111+ message : `Deploy ${ colors . blueBright ( projectPath ( ) ) } to production instead?`
112+ } )
113+ if ( ! shouldDeploy || isCancel ( shouldDeploy ) ) {
114+ return consola . log ( 'Cancelled.' )
115+ }
116+ }
117+
108118 // #region Build
109119 if ( args . build ) {
110120 consola . info ( 'Building the Nuxt project...' )
@@ -135,6 +145,11 @@ export default defineCommand({
135145 const fileKeys = await storage . getKeys ( )
136146 const pathsToDeploy = getPathsToDeploy ( fileKeys )
137147 const config = await storage . getItem ( 'hub.config.json' )
148+ if ( ! config . nitroPreset && linkedProject . type === 'worker' ) {
149+ consola . error ( 'Please upgrade `@nuxthub/core` to the latest version to deploy to a worker project.' )
150+ process . exit ( 1 )
151+ }
152+ const isWorkerPreset = [ 'cloudflare_module' , 'cloudflare_durable' , 'cloudflare-module' , 'cloudflare-durable' ] . includes ( config . nitroPreset )
138153 const { format : formatNumber } = new Intl . NumberFormat ( 'en-US' )
139154
140155 let spinner = ora ( `Preparing ${ colors . blueBright ( linkedProject . slug ) } deployment for ${ deployEnvColored } ...` ) . start ( )
@@ -145,40 +160,64 @@ export default defineCommand({
145160 spinnerColorIndex = ( spinnerColorIndex + 1 ) % spinnerColors . length
146161 } , 2500 )
147162
148- let deploymentKey , serverFiles , metaFiles
163+ let deploymentKey , serverFiles , metaFiles , completionToken
149164 try {
150- const publicFiles = await getPublicFiles ( storage , pathsToDeploy )
151-
152- const deploymentInfo = await $api ( `/teams/${ linkedProject . teamSlug } /projects/${ linkedProject . slug } /${ deployEnv } /deploy/prepare` , {
165+ let url = `/teams/${ linkedProject . teamSlug } /projects/${ linkedProject . slug } /${ deployEnv } /deploy/prepare`
166+ let publicFiles , publicManifest
167+
168+ if ( isWorkerPreset ) {
169+ url = `/teams/${ linkedProject . teamSlug } /projects/${ linkedProject . slug } /${ deployEnv } /deploy/worker/prepare`
170+ publicFiles = await getWorkerPublicFiles ( storage , pathsToDeploy )
171+ /**
172+ * { "/index.html": { hash: "hash", size: 30 }
173+ */
174+ publicManifest = publicFiles . reduce ( ( acc , file ) => {
175+ acc [ file . path ] = {
176+ hash : file . hash ,
177+ size : file . size
178+ }
179+ return acc
180+ } , { } )
181+ } else {
182+ publicFiles = await getPublicFiles ( storage , pathsToDeploy )
183+ /**
184+ * { "/index.html": "hash" }
185+ */
186+ publicManifest = publicFiles . reduce ( ( acc , file ) => {
187+ acc [ file . path ] = file . hash
188+ return acc
189+ } , { } )
190+ }
191+ // Get deployment info by preparing the deployment
192+ const deploymentInfo = await $api ( url , {
153193 method : 'POST' ,
154194 body : {
155195 config,
156- /**
157- * Public manifest is a map of file paths to their unique hash (SHA256 sliced to 32 characters).
158- * @example
159- * {
160- * "/index.html": "hash",
161- * "/assets/image.png": "hash"
162- * }
163- */
164- publicManifest : publicFiles . reduce ( ( acc , file ) => {
165- acc [ file . path ] = file . hash
166- return acc
167- } , { } )
196+ publicManifest
168197 }
169198 } )
170199 spinner . succeed ( `${ colors . blueBright ( linkedProject . slug ) } ready to deploy.` )
171- const { missingPublicHashes, cloudflareUploadJwt } = deploymentInfo
172200 deploymentKey = deploymentInfo . deploymentKey
201+
202+ const { cloudflareUploadJwt, buckets, accountId } = deploymentInfo
203+ // missingPublicHash is sent for pages & buckets for worker
204+ let missingPublicHashes = deploymentInfo . missingPublicHashes || buckets . flat ( )
173205 const publicFilesToUpload = publicFiles . filter ( file => missingPublicHashes . includes ( file . hash ) )
174206
175207 if ( publicFilesToUpload . length ) {
176208 const totalSizeToUpload = publicFilesToUpload . reduce ( ( acc , file ) => acc + file . size , 0 )
177209 spinner = ora ( `Uploading ${ colors . blueBright ( formatNumber ( publicFilesToUpload . length ) ) } new static assets (${ colors . blueBright ( prettyBytes ( totalSizeToUpload ) ) } )...` ) . start ( )
178- await uploadAssetsToCloudflare ( publicFilesToUpload , cloudflareUploadJwt , ( { progressSize, totalSize } ) => {
179- const percentage = Math . round ( ( progressSize / totalSize ) * 100 )
180- spinner . text = `${ percentage } % uploaded (${ prettyBytes ( progressSize ) } /${ prettyBytes ( totalSize ) } )...`
181- } )
210+ if ( linkedProject . type === 'pages' ) {
211+ await uploadAssetsToCloudflare ( publicFilesToUpload , cloudflareUploadJwt , ( { progressSize, totalSize } ) => {
212+ const percentage = Math . round ( ( progressSize / totalSize ) * 100 )
213+ spinner . text = `${ percentage } % uploaded (${ prettyBytes ( progressSize ) } /${ prettyBytes ( totalSize ) } )...`
214+ } )
215+ } else {
216+ completionToken = await uploadWorkersAssetsToCloudflare ( accountId , publicFilesToUpload , cloudflareUploadJwt , ( { progressSize, totalSize } ) => {
217+ const percentage = Math . round ( ( progressSize / totalSize ) * 100 )
218+ spinner . text = `${ percentage } % uploaded (${ prettyBytes ( progressSize ) } /${ prettyBytes ( totalSize ) } )...`
219+ } )
220+ }
182221 spinner . succeed ( `${ colors . blueBright ( formatNumber ( publicFilesToUpload . length ) ) } new static assets uploaded (${ colors . blueBright ( prettyBytes ( totalSizeToUpload ) ) } )` )
183222 }
184223
@@ -188,8 +227,14 @@ export default defineCommand({
188227 consola . info ( `${ colors . blueBright ( formatNumber ( publicFiles . length ) ) } static assets (${ colors . blueBright ( prettyBytes ( totalSize ) ) } / ${ colors . blueBright ( prettyBytes ( totalGzipSize ) ) } gzip)` )
189228 }
190229
191- metaFiles = await Promise . all ( pathsToDeploy . filter ( isMetaPath ) . map ( p => getFile ( storage , p , 'base64' ) ) )
192- serverFiles = await Promise . all ( pathsToDeploy . filter ( isServerPath ) . map ( p => getFile ( storage , p , 'base64' ) ) )
230+ metaFiles = await Promise . all ( pathsToDeploy . filter ( isWorkerPreset ? isWorkerMetaPath : isMetaPath ) . map ( p => getFile ( storage , p , 'base64' ) ) )
231+ serverFiles = await Promise . all ( pathsToDeploy . filter ( isWorkerPreset ? isWorkerServerPath : isServerPath ) . map ( p => getFile ( storage , p , 'base64' ) ) )
232+ if ( isWorkerPreset ) {
233+ serverFiles = serverFiles . map ( file => ( {
234+ ...file ,
235+ path : file . path . replace ( '/server/' , '/' )
236+ } ) )
237+ }
193238 const serverFilesSize = serverFiles . reduce ( ( acc , file ) => acc + file . size , 0 )
194239 const serverFilesGzipSize = serverFiles . reduce ( ( acc , file ) => acc + file . gzipSize , 0 )
195240 consola . info ( `${ colors . blueBright ( formatNumber ( serverFiles . length ) ) } server files (${ colors . blueBright ( prettyBytes ( serverFilesSize ) ) } / ${ colors . blueBright ( prettyBytes ( serverFilesGzipSize ) ) } gzip)...` )
@@ -284,13 +329,14 @@ export default defineCommand({
284329
285330 // #region Complete deployment
286331 spinner = ora ( `Deploying ${ colors . blueBright ( linkedProject . slug ) } to ${ deployEnvColored } ...` ) . start ( )
287- const deployment = await $api ( `/teams/${ linkedProject . teamSlug } /projects/${ linkedProject . slug } /${ deployEnv } /deploy/complete` , {
332+ const deployment = await $api ( `/teams/${ linkedProject . teamSlug } /projects/${ linkedProject . slug } /${ deployEnv } /deploy/${ isWorkerPreset ? 'worker/ complete' : 'complete' } ` , {
288333 method : 'POST' ,
289334 body : {
290335 deploymentKey,
291336 git,
292337 serverFiles,
293- metaFiles
338+ metaFiles,
339+ completionToken
294340 } ,
295341 } ) . catch ( ( err ) => {
296342 spinner . fail ( `Failed to deploy ${ colors . blueBright ( linkedProject . slug ) } to ${ deployEnvColored } .` )
0 commit comments