-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update: add json api with optional parameters
- Loading branch information
1 parent
cfdcf15
commit ec976b7
Showing
8 changed files
with
249 additions
and
63 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
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,14 @@ | ||
package db | ||
|
||
type Meta struct { | ||
Title string `json:"title"` | ||
Description string `json:"description"` | ||
Favicon []byte `json:"favicon"` | ||
} | ||
|
||
type Bookmark struct { | ||
Meta Meta `json:"meta"` | ||
URL string `json:"url" binding:"required"` | ||
Created int `json:"created"` | ||
LastUpdated int `json:"last_updated"` | ||
} |
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
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 @@ | ||
package handlers |
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,102 @@ | ||
package handlers | ||
|
||
import ( | ||
DB "bingo/api/db" | ||
"bingo/api/utils" | ||
"database/sql" | ||
"errors" | ||
"io" | ||
"log" | ||
"net/http" | ||
"regexp" | ||
"strings" | ||
"time" | ||
|
||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
var db *sql.DB = DB.GetDB() | ||
|
||
func getMetadataFromURL(url string, availableMetadata DB.Meta) (meta *DB.Meta, err error) { | ||
|
||
url = strings.TrimSpace(url) | ||
if !(strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "https://")) { | ||
url = "https://" + url | ||
} | ||
// TODO check url before passing into http | ||
res, err := http.Get(url) | ||
if err != nil { | ||
return nil, errors.New("Invalid URL.") | ||
} | ||
|
||
defer res.Body.Close() | ||
|
||
hm := new(DB.Meta) | ||
data, _ := io.ReadAll(res.Body) | ||
headRegex := regexp.MustCompile("<head>((.|\n|\r\n)+)</head>") | ||
|
||
head := string(headRegex.Find(data)) | ||
head = strings.ReplaceAll(head, "\n", "") | ||
|
||
if availableMetadata.Title == "" { | ||
titleRegex := regexp.MustCompile(`<title.*>(.+)<\/title>`) | ||
metaTitleRegex := regexp.MustCompile(`<meta.*?property="og:title".*?content="(.+?)".*?\/?>`) | ||
titleMatches := titleRegex.FindStringSubmatch(head) | ||
if len(titleMatches) == 0 { | ||
titleMatches = metaTitleRegex.FindStringSubmatch(head) | ||
} | ||
|
||
if len(titleMatches) == 0 { | ||
hm.Title = "" | ||
} else { | ||
hm.Title = titleMatches[1] | ||
} | ||
} else { | ||
hm.Title = availableMetadata.Title | ||
} | ||
|
||
if availableMetadata.Description == "" { | ||
|
||
descriptionRegex := regexp.MustCompile(`<meta.*?(?:name="description"|property="og:description").*?content="(.*?)".*?\/>`) | ||
descMatches := descriptionRegex.FindStringSubmatch(head) | ||
if len(descMatches) == 0 { | ||
hm.Description = "" | ||
} else { | ||
hm.Description = descMatches[1] | ||
} | ||
} else { | ||
hm.Description = availableMetadata.Description | ||
} | ||
hm.Favicon = availableMetadata.Favicon | ||
return hm, nil | ||
} | ||
|
||
func AddBookmark(ctx *gin.Context) { | ||
var json DB.Bookmark | ||
if err := ctx.ShouldBindJSON(&json); err != nil { | ||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
meta, err := getMetadataFromURL(json.URL, json.Meta) | ||
statement, err := db.Prepare("INSERT INTO meta (title, description, favicon) VALUES (?, ?, ?)") | ||
utils.Must(err) | ||
defer statement.Close() | ||
info, err := statement.Exec(meta.Title, meta.Description, meta.Favicon) | ||
metaID, _ := info.LastInsertId() | ||
|
||
if err != nil { | ||
log.Println(err) | ||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid URL."}) | ||
return | ||
} | ||
|
||
statement, err = db.Prepare("INSERT INTO bookmarks (url, meta, created, last_updated) VALUES (?, ?, ?, ?)") | ||
utils.Must(err) | ||
defer statement.Close() | ||
|
||
now := time.Now().Unix() | ||
_, err = statement.Exec(json.URL, metaID, now, now) | ||
utils.Must(err) | ||
|
||
ctx.String(http.StatusOK, "Saved URL.") | ||
} |
This file was deleted.
Oops, something went wrong.
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
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,100 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"net/url" | ||
"regexp" | ||
"strings" | ||
|
||
"io" | ||
) | ||
|
||
func main() { | ||
|
||
http.HandleFunc(`/read`, func(rw http.ResponseWriter, req *http.Request) { | ||
rw.Header().Set(`Content-Type`, `application/json`) | ||
|
||
err := req.ParseForm() | ||
if err != nil { | ||
rw.WriteHeader(http.StatusBadRequest) | ||
json.NewEncoder(rw).Encode(map[string]string{"error": err.Error()}) | ||
return | ||
} | ||
|
||
link := req.FormValue(`link`) | ||
if link == "" { | ||
rw.WriteHeader(http.StatusBadRequest) | ||
json.NewEncoder(rw).Encode(map[string]string{"error": `empty value of link`}) | ||
return | ||
} | ||
|
||
if _, err := url.Parse(link); err != nil { | ||
rw.WriteHeader(http.StatusBadRequest) | ||
json.NewEncoder(rw).Encode(map[string]string{"error": err.Error()}) | ||
return | ||
} | ||
|
||
resp, err := http.Get(link) | ||
if err != nil { | ||
//proxy status and err | ||
rw.WriteHeader(resp.StatusCode) | ||
json.NewEncoder(rw).Encode(map[string]string{"error": err.Error()}) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
|
||
meta := extract(resp.Body) | ||
rw.WriteHeader(http.StatusOK) | ||
json.NewEncoder(rw).Encode(meta) | ||
return | ||
}) | ||
|
||
// little help %) | ||
println("call like: \n$ curl -XPOST 'http://localhost:4567/read' -d link='https://github.com/golang/go'") | ||
|
||
err := http.ListenAndServe(`:4567`, nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
} | ||
|
||
type HTMLMeta struct { | ||
Title string `json:"title"` | ||
Description string `json:"description"` | ||
Favicon string `json:"favicon"` | ||
} | ||
|
||
func extract(resp io.Reader) *HTMLMeta { | ||
hm := new(HTMLMeta) | ||
data, _ := io.ReadAll(resp) | ||
headRegex := regexp.MustCompile("<head>((.|\n|\r\n)+)</head>") | ||
|
||
head := string(headRegex.Find(data)) | ||
head = strings.ReplaceAll(head, "\n", "") | ||
|
||
titleRegex := regexp.MustCompile(`<title.*>(.+)<\/title>`) | ||
metaTitleRegex := regexp.MustCompile(`<meta.*?property="og:title".*?content="(.+?)".*?\/?>`) | ||
descriptionRegex := regexp.MustCompile(`<meta.*?(?:name="description"|property="og:description").*?content="(.*?)".*?\/>`) | ||
|
||
descMatches := descriptionRegex.FindStringSubmatch(head) | ||
titleMatches := titleRegex.FindStringSubmatch(head) | ||
if len(titleMatches) == 0 { | ||
titleMatches = metaTitleRegex.FindStringSubmatch(head) | ||
} | ||
|
||
if len(descMatches) == 0 { | ||
hm.Description = "" | ||
} else { | ||
hm.Description = descMatches[1] | ||
} | ||
|
||
if len(titleMatches) == 0 { | ||
hm.Title = "" | ||
} else { | ||
hm.Title = titleMatches[1] | ||
} | ||
|
||
return hm | ||
} |