-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(major):refactoring/code splitting
- Loading branch information
1 parent
b92410d
commit 10b6ad4
Showing
13 changed files
with
1,103 additions
and
1,050 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,4 @@ import "fmt" | |
|
||
func another() { | ||
fmt.Println("another") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package main | ||
|
||
func addBlogConfigurations(meta map[string]interface{}) map[string]interface{} { | ||
|
||
if config == nil { | ||
log.Fatal("Configuration is not initialized. Call loadGlobalConfig to initialize it.") | ||
} | ||
|
||
globalSidebarTOC := config.GetDefault("blog-configuration.SIDEBAR_TOC", "").(bool) | ||
globalFeatured := config.GetDefault("blog-configuration.FEATURED", "").(bool) | ||
globalStatus := config.GetDefault("blog-configuration.STATUS", "").(string) | ||
if _, ok := meta["featured"]; !ok { | ||
meta["featured"] = globalFeatured | ||
} | ||
if _, ok := meta["status"]; !ok { | ||
meta["status"] = globalStatus | ||
} | ||
if meta["status"] == nil || meta["featured"] == nil { | ||
log.Error("required featured and status") | ||
} | ||
|
||
defaultStyle := `pre { line-height: 125%; } | ||
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } | ||
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } | ||
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } | ||
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } | ||
.article-image { | ||
max-width: 600px; | ||
margin: 0 auto !important; | ||
float: none !important; | ||
}` | ||
|
||
sidebarTocHead := `<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.css"> | ||
<style> | ||
.gh-content { | ||
position: relative; | ||
} | ||
.gh-toc > .toc-list { | ||
position: relative; | ||
} | ||
.toc-list { | ||
overflow: hidden; | ||
list-style: none; | ||
} | ||
@media (min-width: 1300px) { | ||
.gh-sidebar { | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
margin-top: 4vmin; | ||
margin-left: 20px; | ||
grid-column: wide-end / main-end; /* Place the TOC to the right of the content */ | ||
width: inline-block; | ||
white-space: nowrap; | ||
} | ||
.gh-toc-container { | ||
position: sticky; /* On larger screens, TOC will stay in the same spot on the page */ | ||
top: 4vmin; | ||
} | ||
} | ||
.gh-toc .is-active-link::before { | ||
background-color: var(--ghost-accent-color); /* Defines TOC accent color based on Accent color set in Ghost Admin */ | ||
} | ||
</style>` | ||
sidebarTocFooter := `<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script> | ||
<script> | ||
const parent = document.querySelector(".gh-content.gh-canvas"); | ||
// Create the <aside> element | ||
const asideElement = document.createElement("aside"); | ||
asideElement.setAttribute("class", "gh-sidebar"); | ||
//asideElement.style.zIndex = 0; // sent to back so it doesn't show on top of images | ||
// Create the container div for title and TOC | ||
const containerElement = document.createElement("div"); | ||
containerElement.setAttribute("class", "gh-toc-container"); | ||
// Create the title element | ||
const titleElement = document.createElement("div"); | ||
titleElement.textContent = "Table of Contents"; | ||
titleElement.style.fontWeight = "bold"; | ||
containerElement.appendChild(titleElement); | ||
// Create the <div> element for TOC | ||
const divElement = document.createElement("div"); | ||
divElement.setAttribute("class", "gh-toc"); | ||
containerElement.appendChild(divElement); | ||
// Append the <div> element to the <aside> element | ||
asideElement.appendChild(containerElement); | ||
parent.insertBefore(asideElement, parent.firstChild); | ||
tocbot.init({ | ||
// Where to render the table of contents. | ||
tocSelector: '.gh-toc', | ||
// Where to grab the headings to build the table of contents. | ||
contentSelector: '.gh-content', | ||
// Which headings to grab inside of the contentSelector element. | ||
headingSelector: 'h1, h2, h3, h4', | ||
// Ensure correct positioning | ||
hasInnerContainers: true, | ||
}); | ||
// Get the table of contents element | ||
const toc = document.querySelector(".gh-toc"); | ||
const sidebar = document.querySelector(".gh-sidebar"); | ||
// Check the number of items in the table of contents | ||
const tocItems = toc.querySelectorAll('li').length; | ||
// Only show the table of contents if it has more than 5 items | ||
if (tocItems > 2) { | ||
sidebar.style.display = 'block'; | ||
} else { | ||
sidebar.style.display = 'none'; | ||
} | ||
</script>` | ||
|
||
if existingHead, ok := meta["codeinjection_head"].(string); ok { | ||
meta["codeinjection_head"] = existingHead + "<style>" + defaultStyle + "</style>" | ||
} else { | ||
meta["codeinjection_head"] = "<style> " + defaultStyle + "</style>" | ||
} | ||
blogMetaSidebarTOC := meta["sidebar_toc"] | ||
|
||
if blogMetaSidebarTOC != nil { | ||
globalSidebarTOC = blogMetaSidebarTOC.(bool) | ||
} | ||
if globalSidebarTOC { | ||
if existingHead, ok := meta["codeinjection_head"].(string); ok { | ||
meta["codeinjection_head"] = existingHead + sidebarTocHead | ||
log.Debug("Done Sidebar TOC Code injection") | ||
} else { | ||
meta["codeinjection_head"] = sidebarTocHead | ||
} | ||
meta["codeinjection_foot"] = sidebarTocFooter | ||
} | ||
|
||
return meta | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package main | ||
|
||
import "path/filepath" | ||
|
||
func uploadFeatureImage(meta map[string]interface{}, token string, featureImage string) { | ||
if featureImagePath, ok := meta["feature_image"].(string); ok { | ||
hashValue, err := sha256Sum(featureImagePath) | ||
if err != nil { | ||
log.Error("Error calculating SHA-256 sum:", err) | ||
return | ||
} | ||
|
||
fileExtension := filepath.Ext(featureImagePath) | ||
imageName := hashValue + fileExtension | ||
imageBackend := config.GetDefault("image-configuration.IMAGE_BACKEND", "").(string) | ||
|
||
if imageBackend == "s3" { | ||
log.Debug("Uploading feature image to AWS S3") | ||
meta["feature_image"], err = uploadToS3(featureImagePath, imageName) | ||
if err != nil { | ||
log.Error("Error uploading file to S3:", err) | ||
} else { | ||
log.Info("Images uploading to S3") | ||
} | ||
} else { | ||
log.Debug("Uploading feature image to Ghost Database") | ||
var featureImgList []string | ||
if featureImage != "" { | ||
featureImgList = []string{featureImage} | ||
} else { | ||
featureImgList = []string{} | ||
} | ||
meta["feature_image"], err = uploadToGhost(token, featureImagePath, imageName, featureImgList) | ||
if err != nil { | ||
log.Error("Error uploading feature image to Ghost:", err) | ||
} | ||
|
||
} | ||
|
||
log.Info("Uploaded feature image") | ||
} else { | ||
log.Info("Feature image not provided") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/golang-jwt/jwt" | ||
) | ||
|
||
type Post struct { | ||
ID string `json:"id"` | ||
UpdatedAt string `json:"updated_at"` | ||
Mobiledoc string `json:"mobiledoc"` | ||
FeatureImage string `json:"feature_image"` | ||
} | ||
|
||
type ResponseData struct { | ||
Posts []Post `json:"posts"` | ||
} | ||
|
||
var postsApiBase string | ||
|
||
func getJWToken() (string, error) { | ||
|
||
ghostAdminKey := config.GetDefault("ghost-configuration.ADMIN_API_KEY", "").(string) | ||
ghostVersion := config.GetDefault("ghost-configuration.GHOST_VERSION", "").(string) | ||
|
||
parts := strings.Split(ghostAdminKey, ":") | ||
if len(parts) != 2 { | ||
return "", fmt.Errorf("invalid admin key format") | ||
} | ||
id, secret := parts[0], parts[1] | ||
|
||
var audValue string | ||
if ghostVersion == "v5" { | ||
audValue = "/admin/" | ||
} else { | ||
audValue = "/" + ghostVersion + "/admin/" | ||
} | ||
|
||
iat := time.Now().Unix() | ||
exp := iat + 5*60 // expires in 5 minutes | ||
|
||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ | ||
"iat": iat, | ||
"exp": exp, | ||
"aud": audValue, | ||
}) | ||
|
||
token.Header["kid"] = id | ||
|
||
secretBytes, err := hex.DecodeString(secret) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to decode secret: %v", err) | ||
} | ||
|
||
signedToken, err := token.SignedString(secretBytes) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to sign token: %v", err) | ||
} | ||
|
||
return signedToken, nil | ||
} | ||
|
||
func sha256Sum(filename string) (string, error) { | ||
h := sha256.New() | ||
|
||
file, err := os.Open(filename) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
if _, err := io.Copy(h, file); err != nil { | ||
return "", err | ||
} | ||
|
||
hashInBytes := h.Sum(nil) | ||
hash := hex.EncodeToString(hashInBytes) | ||
|
||
return hash, nil | ||
} | ||
|
||
func getPostId(slug string, headers http.Header) (*Post, error) { | ||
ghostVersion := config.GetDefault("ghost-configuration.GHOST_VERSION", "").(string) | ||
ghostUrl := config.GetDefault("ghost-configuration.GHOST_URL", "").(string) | ||
|
||
if ghostVersion == "v5" { | ||
postsApiBase = ghostUrl + "/api/admin/posts/" | ||
} else { | ||
postsApiBase = ghostUrl + "/api/" + ghostVersion + "/admin/posts/" | ||
} | ||
|
||
client := &http.Client{} | ||
req, err := http.NewRequest("GET", postsApiBase+"slug/"+slug+"/", nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
req.Header = headers | ||
|
||
resp, err := client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
if resp.StatusCode == http.StatusNotFound { | ||
return nil, nil | ||
} | ||
return nil, fmt.Errorf("unable to communicate with the Ghost Admin API: %s", body) | ||
} | ||
|
||
var data ResponseData | ||
err = json.Unmarshal(body, &data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(data.Posts) > 0 { | ||
return &data.Posts[0], nil | ||
} | ||
|
||
return nil, errors.New("no posts found for the given slug") | ||
} | ||
|
||
func makeRequest(headers http.Header, body map[string]interface{}, pid string, updated_at string) { | ||
var method, apiEndpoint string | ||
ghostVersion := config.GetDefault("ghost-configuration.GHOST_VERSION", "").(string) | ||
ghostUrl := config.GetDefault("ghost-configuration.GHOST_URL", "").(string) | ||
|
||
if ghostVersion == "v5" { | ||
postsApiBase = ghostUrl + "/api/admin/posts/" | ||
} else { | ||
postsApiBase = ghostUrl + "/api/" + ghostVersion + "/admin/posts/" | ||
} | ||
if pid == "" { | ||
method = http.MethodPost | ||
apiEndpoint = postsApiBase + "?source=html" | ||
} else { | ||
method = http.MethodPut | ||
body["posts"].([]map[string]interface{})[0]["updated_at"] = updated_at | ||
apiEndpoint = postsApiBase + pid + "?source=html" | ||
} | ||
|
||
client := &http.Client{} | ||
bodyJson, err := json.Marshal(body) | ||
if err != nil { | ||
log.Error("Error marshaling body to JSON:", err) | ||
return | ||
} | ||
|
||
req, err := http.NewRequest(method, apiEndpoint, bytes.NewBuffer(bodyJson)) | ||
if err != nil { | ||
log.Error("Error creating request:", err) | ||
return | ||
} | ||
req.Header = headers | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
resp, err := client.Do(req) | ||
if err != nil { | ||
log.Error("Error executing request:", err) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { | ||
var errorMessage string | ||
errorBytes, _ := io.ReadAll(resp.Body) | ||
if err := json.Unmarshal(errorBytes, &errorMessage); err != nil { | ||
errorMessage = string(errorBytes) | ||
} | ||
log.Error("Request failed with status: %s. Error message: %s\n", resp.Status, errorMessage) | ||
return | ||
} | ||
|
||
// Unmarshal the response | ||
var responseData map[string]interface{} | ||
err = json.NewDecoder(resp.Body).Decode(&responseData) | ||
if err != nil { | ||
log.Error("Error unmarshaling response:", err) | ||
return | ||
} | ||
|
||
// Log the result | ||
if pid == "" { | ||
log.Info("Created new post") | ||
} else { | ||
log.Info("Updated existing post based on slug") | ||
} | ||
fmt.Printf("Blog preview link: %s\n", responseData["posts"].([]interface{})[0].(map[string]interface{})["url"]) | ||
} |
Oops, something went wrong.