// 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 }