Created
April 29, 2020 11:39
-
-
Save petitviolet/a2a601f06ae20f98d1306e8690d358bd to your computer and use it in GitHub Desktop.
oEmbed API for blog.petitviolet.net
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
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