Skip to content

Instantly share code, notes, and snippets.

@petitviolet
Created April 29, 2020 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petitviolet/a2a601f06ae20f98d1306e8690d358bd to your computer and use it in GitHub Desktop.
Save petitviolet/a2a601f06ae20f98d1306e8690d358bd to your computer and use it in GitHub Desktop.
oEmbed API for blog.petitviolet.net
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"strings"
"github.com/mattn/go-jsonpointer"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World")
}
func main() {
host := "0.0.0.0"
port := os.Getenv("PORT")
if port == "" {
// local
port = "8080"
host = "127.0.0.1"
}
http.HandleFunc("/oembed", oEmbedHandler)
http.HandleFunc("/", handler)
log.Printf("Listening on port %s:%s", host, port)
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%s", host, port), nil))
}
func oEmbedHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Handle HTTP request %s\n", r.URL)
target := r.URL.Query()["url"][0]
data, err := newOEmbedData(target)
if err != nil {
body, _ := json.Marshal(map[string]string{"error": err.Error()})
w.WriteHeader(http.StatusNotFound)
w.Header().Set("Content-Type", "application/json")
w.Write(body)
} else {
body, _ := json.Marshal(data)
w.Header().Set("Content-Type", "application/json")
w.Write(body)
}
}
type oEmbedData struct {
Url string `json:"url"`
Title string `json:"title"`
Tags []string `json:"tags"`
Description string `json:"description"`
Html string `json:"html"`
Published string `json:"published"`
BlogUrl string `json:"blog_url"`
BlogTitle string `json:"blog_title"`
AuthorUrl string `json:"author_url"`
AuthorName string `json:"author_name"`
ProviderName string `json:"provider_name"`
ProviderUrl string `json:"provider_url"`
ImageUrl string `json:"image_url"`
Width string `json:"width"`
Height string `json:"height"`
Version string `json:"version"`
Type string `json:"type"`
}
func newOEmbedData(url string) (*oEmbedData, error) {
post, err := findPost(url)
if err != nil {
log.Println(err.Error())
return nil, err
}
oEmbedData := oEmbedData{
Url: url,
Title: post.Title,
Description: post.Description,
Tags: post.Tags,
Published: post.Date,
Html: buildHtml(url, post),
BlogUrl: "https://blog.petitviolet.net",
BlogTitle: "blog.petitviolet.net",
AuthorUrl: "https://petitviolet.net",
AuthorName: "petitviolet",
ProviderName: "blog.petitviolet.net",
ProviderUrl: "https://blog.petitviolet.net",
ImageUrl: "https://s.gravatar.com/avatar/93bc8fb48f57c11e417dad9d26a2fb8a?s=480",
Width: "100%",
Height: "200px",
Version: "1.0",
Type: "rich",
}
return &oEmbedData, nil
}
type Post struct {
Title string
Tags []string
Description string
Excerpt string
Date string
}
var urlPattern = regexp.MustCompile(`^https://blog.petitviolet.net/post/(\d{4}-\d{2}-\d{2})/(.+)$`)
func findPost(url string) (*Post, error) {
res := urlPattern.FindStringSubmatch(url)
if len(res) != 3 {
return nil, fmt.Errorf("Invalid url. url: %s", url)
}
pageDataPath := buildPageDatePath(res[1], res[2])
raw, err := ioutil.ReadFile(pageDataPath)
if err != nil {
err = fmt.Errorf("Failed to read file. file = %s, err = %s", pageDataPath, err.Error())
return nil, err
}
return buildPost(raw), nil
}
func buildHtml(url string, post *Post) string {
id := strings.Replace(post.Title, " ", "-", -1)
return fmt.Sprintf(`
<style type="text/css">
#oembed-container-%s {
border: 1px solid #AAA;
padding: 0.4em 1em 0.4em 1em;
display: flex;
flex-direction: column;
box-shadow: 0px 0px 2px 2px rgba(100, 100, 100, 0.1);
width: 80%%;
color: #000;
background-color: #FEFEFE;
}
#oembed-body-%s {
display: flex;
flex-direction: column;
align-content: space-between;
}
#oembed-body-link-%s {
text-decoration: underline;
color: #5555FF;
font-size: 18px;
font-weight: 700;
}
#oembed-body-description-%s {
font-weight: 500;
font-size: 16px;
}
#oembed-body-excerpt-%s {
font-size: 10px;
color: #444;
}
#oembed-footer-%s {
display: flex;
margin-top: 8px;
align-items: center;
}
#oembed-footer-logo-%s {
margin-left: 0;
margin-right: 16px;
margin-top: 0;
margin-bottom: 0;
}
#oembed-footer-text-%s {
}
#oembed-footer-link-%s {
text-decoration: underline;
color: #5555FF;
display: block;
font-size: 16px;
}
#oembed-footer-text-meta-%s {
display: block;
font-size: 12px;
}
</style>
<div id="oembed-container-%s">
<div id="oembed-body-%s"> <!-- body -->
<span>
<a id="oembed-body-link-%s"
href="%s" target="_blank" rel="noopener noreferrer">
%s
</a>
</span>
<span id="oembed-body-description-%s">%s</span>
<span id="oembed-body-excerpt-%s">%s</span>
</div>
<div id="oembed-footer-%s"> <!-- footer -->
<img id="oembed-footer-logo-%s"
src="https://s.gravatar.com/avatar/93bc8fb48f57c11e417dad9d26a2fb8a?s=44" />
<div id="oembed-footer-text-%s">
<a id="oembed-footer-link-%s"
href="https://blog.petitviolet.net">
blog.petitviolet.net
</a>
<div id="oembed-footer-text-meta-%s">
%s [%s]
</div>
</div>
</div>
</div>
`, id, id, id, id, id, id, // CSS
id, id, id, id, id, id, id, // CSS
url, post.Title,
id, post.Description,
id, post.Excerpt,
id, id, id, id, id, // CSS
post.Date, strings.Join(post.Tags, ", "))
}
func buildPost(raw []byte) *Post {
var pageData map[string]interface{}
json.Unmarshal(raw, &pageData)
title, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/title")
description, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/description")
excerpt, _ := jsonpointer.Get(pageData, "/result/data/post/excerpt")
date, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/date")
_tags, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/tags")
tagIs := _tags.([]interface{})
tagStrs := make([]string, len(tagIs))
for i, v := range tagIs {
tagStrs[i] = v.(string)
}
// fmt.Printf("pageDataPath: %s\n", pageDataPath)
// fmt.Printf("raw: %s\n", raw)
// fmt.Printf("title: %v\n", title)
// fmt.Printf("description: %v\n", description)
// fmt.Printf("date: %v\n", date)
// fmt.Printf("tags: %v\n", tagStrs)
post := Post{
Title: title.(string),
Description: description.(string),
Excerpt: excerpt.(string),
Date: date.(string),
Tags: tagStrs,
}
return &post
}
func buildPageDatePath(date string, name string) string {
return fmt.Sprintf("public/page-data/post/%s/%s/page-data.json", date, name)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment