SeekiaWebsite/main.go

362 lines
10 KiB
Go

// 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 (Seekia.net) domain.
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.71.tgz"
// This is the IPFS base32 CID for the current Seekia release .tgz file
const currentReleaseIpfsCID = "bafybeihitnnhnlfkwvmndkxebd3sy5dqko5ofiazjktjsjrepyyxqouwqa"
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, {{.BeGeneticsAware}} will be replaced with "Be Genetics 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 {{.BaseURL}} 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 }
templateDefinitionsMap["BaseURL"] = ".."
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
}