Skip to content

Commit 8adfa27

Browse files
committed
cont
0 parents  commit 8adfa27

6 files changed

Lines changed: 147 additions & 0 deletions

File tree

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ADDR=":8888"
2+
LINK_PREVIEW_KEY="123456"
3+
4+
# optional ssl certificates
5+
SSL_CERT="cert.pem"
6+
SSL_KEY="cert.key"
7+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.env
2+
v1

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# LinkPreview.net Proxy Server in Go
2+
3+
4+
How to use:
5+
6+
1. Download executable
7+
2. Create .env file configuration, see .env.example
8+
9+
10+
Sample .env file:
11+
12+
```
13+
ADDR="localhost:8000"
14+
LINK_PREVIEW_KEY="123456"
15+
```
16+
17+
3. Start your server
18+
19+
20+

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module linkpreview.net/proxy/v1
2+
3+
go 1.17
4+
5+
require (
6+
github.com/joho/godotenv v1.4.0
7+
github.com/muesli/cache2go v0.0.0-20211005105910-8e46465cca4a
8+
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
2+
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
3+
github.com/muesli/cache2go v0.0.0-20211005105910-8e46465cca4a h1:IZxQOY9gAiiGGuEdlOBnqaC3yumj8UvyQluBNqGP2Ek=
4+
github.com/muesli/cache2go v0.0.0-20211005105910-8e46465cca4a/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc=

main.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"log"
9+
"net/http"
10+
"os"
11+
"time"
12+
13+
"github.com/joho/godotenv"
14+
"github.com/muesli/cache2go"
15+
)
16+
17+
const linkpreviewAPI = "https://api.linkpreview.net"
18+
19+
var linkpreviewKey = ""
20+
21+
var cache *cache2go.CacheTable
22+
23+
type cachedResponse struct {
24+
body []byte
25+
status int
26+
}
27+
28+
type lpreq struct {
29+
Key string `json:"key"`
30+
Q string `json:"q"`
31+
Fields string `json:"fields"`
32+
}
33+
34+
func main() {
35+
36+
err := godotenv.Load()
37+
if err != nil {
38+
log.Fatal("Error loading .env file")
39+
}
40+
41+
addr := os.Getenv("ADDR")
42+
linkpreviewKey = os.Getenv("LINK_PREVIEW_KEY")
43+
44+
cache = cache2go.Cache("myCache")
45+
46+
http.HandleFunc("/", proxyHandler)
47+
48+
if os.Getenv("SSL_CERT") != "" && os.Getenv("SSL_KEY") != "" {
49+
fmt.Printf("Proxy secure server started on https://%s\n", addr)
50+
if err := http.ListenAndServeTLS(addr, os.Getenv("SSL_CERT"), os.Getenv("SSL_KEY"), nil); err != nil {
51+
panic(err)
52+
}
53+
}
54+
55+
fmt.Printf("Proxy server started on http://%s\n", addr)
56+
if err := http.ListenAndServe(addr, nil); err != nil {
57+
panic(err)
58+
}
59+
60+
}
61+
62+
func proxyHandler(w http.ResponseWriter, r *http.Request) {
63+
64+
query := r.URL.Query().Get("q")
65+
66+
// keep results in cache for one day (based on the key scheme)
67+
cacheKey := fmt.Sprintf("%s%d", query, time.Now().Day())
68+
69+
cached, err := cache.Value(cacheKey)
70+
if err == nil {
71+
fmt.Println("Serving from Cache: " + query)
72+
w.WriteHeader(cached.Data().(*cachedResponse).status)
73+
w.Write(cached.Data().(*cachedResponse).body)
74+
return
75+
}
76+
77+
body, _ := json.Marshal(&lpreq{
78+
Key: linkpreviewKey,
79+
Q: query,
80+
Fields: "title,description,image,url", // see https://docs.linkpreview.net/#query-parameters
81+
})
82+
83+
fmt.Println("Requesting from the API: " + query)
84+
client := &http.Client{}
85+
req, _ := http.NewRequest("POST", linkpreviewAPI, bytes.NewBuffer(body))
86+
resp, err := client.Do(req)
87+
if err != nil {
88+
http.Error(w, "Server Error", http.StatusInternalServerError)
89+
return
90+
}
91+
defer resp.Body.Close()
92+
93+
cr := cachedResponse{}
94+
cr.body, err = io.ReadAll(resp.Body)
95+
cr.status = resp.StatusCode
96+
if err != nil {
97+
http.Error(w, "Server Error", http.StatusInternalServerError)
98+
return
99+
}
100+
101+
w.WriteHeader(cr.status)
102+
w.Write(cr.body)
103+
104+
// add to cache and automatically expire unused after one day
105+
cache.Add(cacheKey, 24*time.Hour, &cr)
106+
}

0 commit comments

Comments
 (0)