@@ -12,6 +12,7 @@ import axios from 'axios';
1212export type SiteMetadata = {
1313 bundleName : string ;
1414 bundleLastModified : string ;
15+ coreVersion : string ;
1516} ;
1617
1718export type SiteMetadataCache = {
@@ -41,44 +42,6 @@ export class ExperienceSite {
4142 this . siteName = this . siteName . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) ;
4243 }
4344
44- /**
45- * Get an experience site bundle by site name.
46- *
47- * @param conn - Salesforce connection object.
48- * @param siteName - The name of the experience site.
49- * @returns - The experience site.
50- *
51- * @param siteName
52- * @returns
53- */
54- public static getLocalExpSite ( siteName : string ) : ExperienceSite {
55- const siteJsonPath = path . join ( '.localdev' , siteName . trim ( ) . replace ( ' ' , '_' ) , 'site.json' ) ;
56- const siteJson = fs . readFileSync ( siteJsonPath , 'utf8' ) ;
57- const site = JSON . parse ( siteJson ) as ExperienceSite ;
58- return site ;
59- }
60-
61- /**
62- * Fetches all experience site bundles that are published to MRT.
63- *
64- * @param {Connection } conn - Salesforce connection object.
65- * @returns {Promise<ExperienceSite[]> } - List of experience sites.
66- */
67- public static async getAllPublishedExpSites ( org : Org ) : Promise < ExperienceSite [ ] > {
68- const result = await org
69- . getConnection ( )
70- . query < { Id : string ; Name : string ; LastModifiedDate : string } > (
71- "SELECT Id, Name, LastModifiedDate FROM StaticResource WHERE Name LIKE 'MRT%_'"
72- ) ;
73-
74- // Example of creating ExperienceSite instances
75- const experienceSites : ExperienceSite [ ] = result . records . map (
76- ( record ) => new ExperienceSite ( org , getSiteNameFromStaticResource ( record . Name ) )
77- ) ;
78-
79- return experienceSites ;
80- }
81-
8245 /**
8346 * Fetches all current experience sites
8447 *
@@ -135,7 +98,10 @@ export class ExperienceSite {
13598
13699 // Is the site extracted locally
137100 public isSiteSetup ( ) : boolean {
138- return fs . existsSync ( path . join ( this . getExtractDirectory ( ) , 'ssr.js' ) ) ;
101+ if ( fs . existsSync ( path . join ( this . getExtractDirectory ( ) , 'ssr.js' ) ) ) {
102+ return this . getLocalMetadata ( ) ?. coreVersion === '254' ;
103+ }
104+ return false ;
139105 }
140106
141107 // Is the static resource available on the server
@@ -193,6 +159,7 @@ export class ExperienceSite {
193159 this . metadataCache . remoteMetadata = {
194160 bundleName : staticResource . Name ,
195161 bundleLastModified : staticResource . LastModifiedDate ,
162+ coreVersion : '254' ,
196163 } ;
197164 return this . metadataCache . remoteMetadata ;
198165 }
@@ -226,42 +193,23 @@ export class ExperienceSite {
226193 * @returns path of downloaded site zip
227194 */
228195 public async downloadSite ( ) : Promise < string > {
196+ let retVal ;
229197 if ( process . env . STATIC_MODE !== 'true' ) {
230- const retVal = await this . downloadSiteV2 ( ) ;
231- return retVal ;
198+ // Use sites API to download the site bundle on demand
199+ retVal = await this . downloadSiteApi ( ) ;
232200 } else {
233- const remoteMetadata = await this . getRemoteMetadata ( ) ;
234- if ( ! remoteMetadata ) {
235- throw new SfError ( `No published site found for: ${ this . siteDisplayName } ` ) ;
236- }
237-
238- // Download the site from static resources
239- // eslint-disable-next-line no-console
240- console . log ( '[local-dev] Downloading site...' ) ; // TODO spinner
241- const resourcePath = this . getSiteZipPath ( remoteMetadata ) ;
242- const staticresource = await this . org . getConnection ( ) . metadata . read ( 'StaticResource' , remoteMetadata . bundleName ) ;
243- if ( staticresource ?. content ) {
244- // Save the static resource
245- fs . mkdirSync ( this . getSiteDirectory ( ) , { recursive : true } ) ;
246- const buffer = Buffer . from ( staticresource . content , 'base64' ) ;
247- fs . writeFileSync ( resourcePath , buffer ) ;
248-
249- // Save the site's metadata
250- this . saveMetadata ( remoteMetadata ) ;
251- } else {
252- throw new SfError ( `Error occurred downloading your site: ${ this . siteDisplayName } ` ) ;
253- }
254-
255- return resourcePath ;
201+ // This is for testing purposes only now - not an officially supported external path
202+ retVal = await this . downloadSiteStaticResources ( ) ;
256203 }
204+ return retVal ;
257205 }
258206
259207 /**
260208 * Generate a site bundle on demand and download it
261209 *
262210 * @returns path of downloaded site zip
263211 */
264- public async downloadSiteV2 ( ) : Promise < string > {
212+ public async downloadSiteApi ( ) : Promise < string > {
265213 const remoteMetadata = await this . org
266214 . getConnection ( )
267215 . query < { Id : string ; Name : string ; LastModifiedDate : string ; MasterLabel : string } > (
@@ -273,30 +221,31 @@ export class ExperienceSite {
273221 const theSite = remoteMetadata . records [ 0 ] ;
274222
275223 // Download the site via API
276- // eslint-disable-next-line no-console
277- console . log ( '[local-dev] Downloading site...' ) ; // TODO spinner
278224 const conn = this . org . getConnection ( ) ;
279225 const metadata = {
280226 bundleName : theSite . Name ,
281227 bundleLastModified : theSite . LastModifiedDate ,
228+ coreVersion : '254' ,
282229 } ;
283230 const siteId = theSite . Id ;
284231 const siteIdMinus3 = siteId . substring ( 0 , siteId . length - 3 ) ;
285232 const accessToken = conn . accessToken ;
286233 const instanceUrl = conn . instanceUrl ; // Org URL
287234 if ( ! accessToken ) {
288- throw new SfError ( `Error occurred downloading your site: ${ this . siteDisplayName } ` ) ;
235+ throw new SfError ( `Invalid access token, unable to download site: ${ this . siteDisplayName } ` ) ;
289236 }
290237 const resourcePath = this . getSiteZipPath ( metadata ) ;
291238 try {
292- const apiUrl = `${ instanceUrl } /services/data/v63.0/sites/${ siteIdMinus3 } /preview` ;
239+ // Limit API to published sites for now until we have a patch for the issues with unpublished sites
240+ // TODO switch api back to preview mode after issues are addressed
241+ const apiUrl = `${ instanceUrl } /services/data/v63.0/sites/${ siteIdMinus3 } /preview?published` ;
293242 const response = await axios . get ( apiUrl , {
294243 headers : {
295244 Authorization : `Bearer ${ accessToken } ` ,
296245 } ,
297246 responseType : 'stream' ,
298247 } ) ;
299- fs . mkdirSync ( this . getSiteDirectory ( ) , { recursive : true } ) ;
248+ if ( response . statusText ) fs . mkdirSync ( this . getSiteDirectory ( ) , { recursive : true } ) ;
300249
301250 const fileStream = fs . createWriteStream ( resourcePath ) ;
302251 // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
@@ -307,15 +256,51 @@ export class ExperienceSite {
307256 fileStream . on ( 'error' , reject ) ;
308257 } ) ;
309258 this . saveMetadata ( metadata ) ;
310- } catch ( e ) {
311- // eslint-disable-next-line no-console
312- console . error ( 'failed to download site' , e ) ;
259+ } catch ( error ) {
260+ // Handle axios errors
261+ if ( axios . isAxiosError ( error ) ) {
262+ if ( error . response ) {
263+ // Server responded with non-200 status
264+ throw new SfError (
265+ `Failed to download site: Server responded with status ${ error . response . status } - ${ error . response . statusText } `
266+ ) ;
267+ } else if ( error . request ) {
268+ // Request was made but no response received
269+ throw new SfError ( 'Failed to download site: No response received from server' ) ;
270+ }
271+ }
272+ throw new SfError ( `Failed to download site: ${ this . siteDisplayName } ` ) ;
313273 }
314274
315275 // Save the site's metadata
316276 return resourcePath ;
317277 }
318278
279+ // Deprecated. Only used internally now for testing. Customer sites will no longer be stored in static resources
280+ // and are only available via the API.
281+ public async downloadSiteStaticResources ( ) : Promise < string > {
282+ const remoteMetadata = await this . getRemoteMetadata ( ) ;
283+ if ( ! remoteMetadata ) {
284+ throw new SfError ( `No published site found for: ${ this . siteDisplayName } ` ) ;
285+ }
286+
287+ // Download the site from static resources
288+ const resourcePath = this . getSiteZipPath ( remoteMetadata ) ;
289+ const staticresource = await this . org . getConnection ( ) . metadata . read ( 'StaticResource' , remoteMetadata . bundleName ) ;
290+ if ( staticresource ?. content ) {
291+ // Save the static resource
292+ fs . mkdirSync ( this . getSiteDirectory ( ) , { recursive : true } ) ;
293+ const buffer = Buffer . from ( staticresource . content , 'base64' ) ;
294+ fs . writeFileSync ( resourcePath , buffer ) ;
295+
296+ // Save the site's metadata
297+ this . saveMetadata ( remoteMetadata ) ;
298+ } else {
299+ throw new SfError ( `Error occurred downloading your site: ${ this . siteDisplayName } ` ) ;
300+ }
301+ return resourcePath ;
302+ }
303+
319304 private async getNetworkId ( ) : Promise < string > {
320305 const conn = this . org . getConnection ( ) ;
321306 // Query the Network object for the network with the given site name
@@ -332,6 +317,7 @@ export class ExperienceSite {
332317 }
333318 }
334319
320+ // TODO need to get auth tokens for the builder preview also once API issues are addressed
335321 private async getNewSidToken ( networkId : string ) : Promise < string > {
336322 // Get the connection and access token from the org
337323 const conn = this . org . getConnection ( ) ;
@@ -344,9 +330,6 @@ export class ExperienceSite {
344330
345331 // Make the GET request without following redirects
346332 if ( accessToken ) {
347- // TODO should we try and refresh auth here?
348- // await conn.refreshAuth();
349-
350333 // Call out to the switcher servlet to establish a session
351334 const switchUrl = `${ instanceUrl } /servlet/networks/switch?networkId=${ networkId } ` ;
352335 const cookies = [ `sid=${ accessToken } ` , `oid=${ orgIdMinus3 } ` ] . join ( '; ' ) . trim ( ) ;
@@ -409,17 +392,3 @@ export class ExperienceSite {
409392 return '' ;
410393 }
411394}
412-
413- /**
414- * Return the site name given the name of its static resource bundle
415- *
416- * @param staticResourceName the static resource bundle name
417- * @returns the name of the site
418- */
419- function getSiteNameFromStaticResource ( staticResourceName : string ) : string {
420- const parts = staticResourceName . split ( '_' ) ;
421- if ( parts . length < 5 ) {
422- throw new Error ( `Unexpected static resource name: ${ staticResourceName } ` ) ;
423- }
424- return parts . slice ( 4 ) . join ( ' ' ) ;
425- }
0 commit comments