2024-04-10 20:07:49 +02:00
// This package exports 2 Seekia static sites into two folders: ExportedWebsite_IPFS and ExportedWebsite_Server
// ExportedWebsite_IPFS contains the website that is deployed to the Seekia.eth IPFS domain.
// ExportedWebsite_Server contains the website that is deployed to Seekia's clearnet (.net) and Tor (.onion) domains.
package main
import "SeekiaWebsite/translation"
import "bytes"
import "errors"
import "log"
import "os"
import "strings"
import "text/template"
import goFilepath "path/filepath"
// This is the current Seekia release .tgz filename
const currentReleaseFilename = "Seekia-v0.50.tgz"
// This is the IPFS base32 CID for the current Seekia release .tgz file
const currentReleaseIpfsCID = "bafybeie6nkmucrvo3i7bonmlf77dlna65tjogsq5zmrcpqke7pvdkiumhy"
func main ( ) {
//Inputs:
// -string: The folderpath to export the website to
// -bool: Use this to control if we are exporting the Seekia.eth IPFS site or not
exportWebsite := func ( websiteFolderpath string , siteIsIPFS bool ) error {
err := os . RemoveAll ( websiteFolderpath )
if ( err != nil ) { return err }
err = os . Mkdir ( websiteFolderpath , os . ModePerm )
if ( err != nil ) { return err }
//TODO: Add more languages
//allLanguageCodesList := []string{"en", "es"}
allLanguageCodesList := [ ] string { "en" }
// We create the style.css file
cssPageBytes , err := os . ReadFile ( "./resources/style.css" )
if ( err != nil ) { return err }
cssPageString := string ( cssPageBytes )
cssFilepath := goFilepath . Join ( websiteFolderpath , "style.css" )
err = createFile ( cssFilepath , cssPageString )
if ( err != nil ) { return err }
// We copy various folders
copyFolder := func ( folderName string ) error {
sourceFolderPath := goFilepath . Join ( "./resources/" , folderName )
destinationFolderPath := goFilepath . Join ( websiteFolderpath , folderName )
err := copyFolderRecursively ( sourceFolderPath , destinationFolderPath )
if ( err != nil ) { return err }
return nil
}
err = copyFolder ( "images" )
if ( err != nil ) { return err }
err = copyFolder ( "assets" )
if ( err != nil ) { return err }
err = copyFolder ( "memos" )
if ( err != nil ) { return err }
err = copyFolder ( "signatures" )
if ( err != nil ) { return err }
if ( siteIsIPFS == false ) {
// For our IPFS site, we are using an IPFS link for the download
// For our non-IPFS site, we include the link on the same hosted site
// This is because when viewing an IPFS site, the user's browser downloads the entire website (usually?)
// This would take too long to download if we included the release with the site (several megabytes)
releaseFilepath := goFilepath . Join ( "./resources" , "release" , currentReleaseFilename )
releaseFileBytes , err := os . ReadFile ( releaseFilepath )
if ( err != nil ) {
fileDoesNotExist := os . IsNotExist ( err )
if ( fileDoesNotExist == false ) {
return err
}
return errors . New ( "You must place the Seekia release .tgz file into the ./resources/release folder before exporting the website. This can be an empty file with the correct filename if you want. Error: " + err . Error ( ) )
}
releaseFileString := string ( releaseFileBytes )
destinationFolderPath := goFilepath . Join ( websiteFolderpath , "release" )
err = os . Mkdir ( destinationFolderPath , os . ModePerm )
if ( err != nil ) { return err }
destinationFilepath := goFilepath . Join ( destinationFolderPath , currentReleaseFilename )
err = createFile ( destinationFilepath , releaseFileString )
if ( err != nil ) { return err }
}
getTemplateDefinitionsMap := func ( inputLanguageCode string ) ( map [ string ] string , error ) {
translationsMap , err := translation . GetTranslationsMap ( inputLanguageCode )
if ( err != nil ) { return nil , err }
templateDefinitionsMap := translationsMap
// Now we add code snippets to the map
fileList , err := os . ReadDir ( "./resources/codeSnippets" )
if ( err != nil ) { return nil , err }
for _ , fileObject := range fileList {
fileName := fileObject . Name ( )
filePath := goFilepath . Join ( "./resources/codeSnippets/" , fileName )
fileBytes , err := os . ReadFile ( filePath )
if ( err != nil ) { return nil , err }
snippetString := string ( fileBytes )
snippetName := strings . TrimSuffix ( fileName , ".html" )
codeSnippetKey := "CodeSnippet_" + snippetName
_ , exists := templateDefinitionsMap [ codeSnippetKey ]
if ( exists == true ) {
return nil , errors . New ( "Cannot export website: conflict found between translationsMap and codeSnippets" )
}
templateDefinitionsMap [ codeSnippetKey ] = snippetString
}
// LanguageURLBase is empty for all files except /index.html
templateDefinitionsMap [ "LanguageURLBase" ] = ""
return templateDefinitionsMap , nil
}
// We use this function to parse the .html files to replace the internal template values with actual values
// For example, {{.BeRaceAware}} will be replaced with "Be Race Aware", or the equivalent translated text in the website's current language version
// We have to parse twice for the web pages.
// Code snippets have fields that need to be parsed
// So to process the code snippets, we have to:
// 1. First parse the html page files to add the code snippets/replace any language fields
// 2. Then parse again to replace any language fields and instances of {{.LinkURLBase}} within the code snippets we just added
//
parseTemplateObjectToStringTwice := func ( templateObject * template . Template , definitionsMap map [ string ] string ) ( string , error ) {
parseTemplateObjectToString := func ( inputTemplateObject * template . Template ) ( string , error ) {
inputTemplateObject . Option ( "missingkey=error" )
parsedTemplateBuffer := new ( bytes . Buffer )
err = inputTemplateObject . Execute ( parsedTemplateBuffer , definitionsMap )
if ( err != nil ) { return "" , err }
parsedTemplateString := parsedTemplateBuffer . String ( )
return parsedTemplateString , nil
}
parsedTemplateString_Round1 , err := parseTemplateObjectToString ( templateObject )
if ( err != nil ) { return "" , err }
templateObject2 := template . New ( "Round2" )
_ , err = templateObject2 . Parse ( parsedTemplateString_Round1 )
if ( err != nil ) { return "" , err }
parsedTemplateString_Round2 , err := parseTemplateObjectToString ( templateObject2 )
if ( err != nil ) { return "" , err }
return parsedTemplateString_Round2 , nil
}
{
// First we create /index.html
// Using a language code is optional for the main index of the site
// It is always in English
// For example, Seekia.eth/index.html
templateObject , err := template . ParseFiles ( "./resources/pages/index.html" )
if ( err != nil ) { return err }
templateDefinitionsMap , err := getTemplateDefinitionsMap ( "en" )
if ( err != nil ) { return err }
templateDefinitionsMap [ "BaseURL" ] = "."
templateDefinitionsMap [ "LanguageURLBase" ] = "en/"
indexPageString , err := parseTemplateObjectToStringTwice ( templateObject , templateDefinitionsMap )
if ( err != nil ) { return err }
indexFilepath := goFilepath . Join ( websiteFolderpath , "index.html" )
err = createFile ( indexFilepath , indexPageString )
if ( err != nil ) { return err }
}
// Now we create the rest of the pages for each language
// These pages exist behind a language code
// For example:
// seekia.eth/en/index.html
// seekia.eth/es/download.html
getDownloadSeekiaLink := func ( ) string {
if ( siteIsIPFS == false ) {
result := "../release/" + currentReleaseFilename
return result
}
// The IPFS site does not contain the Seekia release .tgz file
// This is because users (sometimes?) download the entire IPFS site when viewing it,
// so downloading many megabytes would take too long
result := "https://ipfs.io/ipfs/" + currentReleaseIpfsCID
return result
}
downloadSeekiaLink := getDownloadSeekiaLink ( )
for _ , languageCode := range allLanguageCodesList {
// We create the language subfolder
languageSubfolderPath := goFilepath . Join ( websiteFolderpath , languageCode )
err = os . Mkdir ( languageSubfolderPath , os . ModePerm )
if ( err != nil ) { return err }
templateDefinitionsMap , err := getTemplateDefinitionsMap ( languageCode )
if ( err != nil ) { return err }
2024-04-13 19:08:21 +02:00
templateDefinitionsMap [ "BaseURL" ] = ".."
2024-04-10 20:07:49 +02:00
templateDefinitionsMap [ "DownloadSeekiaLink" ] = downloadSeekiaLink
createPage := func ( pageFilename string ) error {
pageTemplateFilepath := goFilepath . Join ( "./resources/pages/" , pageFilename )
pageTemplateObject , err := template . ParseFiles ( pageTemplateFilepath )
if ( err != nil ) { return err }
pageString , err := parseTemplateObjectToStringTwice ( pageTemplateObject , templateDefinitionsMap )
if ( err != nil ) { return err }
pageFilepath := goFilepath . Join ( languageSubfolderPath , pageFilename )
err = createFile ( pageFilepath , pageString )
if ( err != nil ) { return err }
return nil
}
err = createPage ( "index.html" )
if ( err != nil ) { return err }
//TODO: Add the Choose Language page and Choose Language banner bar
// err := createPage("chooseLanguage.html")
// if (err != nil){ return err }
err = createPage ( "download.html" )
if ( err != nil ) { return err }
err = createPage ( "about.html" )
if ( err != nil ) { return err }
err = createPage ( "contribute.html" )
if ( err != nil ) { return err }
err = createPage ( "archive.html" )
if ( err != nil ) { return err }
}
return nil
}
err := exportWebsite ( "./ExportedWebsite_IPFS/" , true )
if ( err != nil ) {
log . Println ( err )
return
}
err = exportWebsite ( "./ExportedWebsite_Server/" , false )
if ( err != nil ) {
log . Println ( err )
return
}
log . Println ( "Seekia's websites have been exported!" )
}
// This function will copy a folder and its subfolders
func copyFolderRecursively ( sourceFolderPath string , destinationFolderPath string ) error {
err := os . Mkdir ( destinationFolderPath , os . ModePerm )
if ( err != nil ) { return err }
filesystemPathsList , err := os . ReadDir ( sourceFolderPath )
if ( err != nil ) { return err }
for _ , filesystemObject := range filesystemPathsList {
pathName := filesystemObject . Name ( )
sourcePathName := goFilepath . Join ( sourceFolderPath , pathName )
destinationPathName := goFilepath . Join ( destinationFolderPath , pathName )
isDirectory := filesystemObject . IsDir ( )
if ( isDirectory == true ) {
err := copyFolderRecursively ( sourcePathName , destinationPathName )
if ( err != nil ) { return err }
} else {
fileBytes , err := os . ReadFile ( sourcePathName )
if ( err != nil ) { return err }
fileString := string ( fileBytes )
err = createFile ( destinationPathName , fileString )
if ( err != nil ) { return err }
}
}
return nil
}
func createFile ( filePath string , fileContents string ) error {
newFile , err := os . Create ( filePath )
_ , err = newFile . WriteString ( fileContents )
if ( err != nil ) { return err }
newFile . Close ( )
return nil
}