initial commit
This commit is contained in:
commit
d6a2316b16
127
bookmark.go
Normal file
127
bookmark.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Bookmarks = []Bookmark{
|
||||||
|
{
|
||||||
|
Title: "DuckDuckGo",
|
||||||
|
Image: "https://duckduckgo.com/assets/logo_homepage.alt.v108.svg",
|
||||||
|
Color: "#1c1c1c",
|
||||||
|
Link: "https://duckduckgo.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Postbank",
|
||||||
|
Image: "https://meine.postbank.de/bundles/@pbs/patternlib_pb/lib/runtime/assets/images/logo-claim.svg",
|
||||||
|
Color: "#ffcc00",
|
||||||
|
Link: "https://meine.postbank.de/#/login",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "YouTube",
|
||||||
|
Image: "https://upload.wikimedia.org/wikipedia/commons/1/1f/YouTube_light_logo_%282017%29.svg",
|
||||||
|
Color: "#ff0000",
|
||||||
|
Link: "https://www.youtube.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Jisho",
|
||||||
|
Image: "https://assets.jisho.org/assets/jisho-logo-v4@2x-7330091c079b9dd59601401b052b52e103978221c8fb6f5e22406d871fcc746a.png",
|
||||||
|
Link: "https://jisho.com/",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Wadoku",
|
||||||
|
Image: "https://www.wadoku.de/img/wadoku_logo.svg",
|
||||||
|
Link: "https://www.wadoku.de/",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Übersetzer",
|
||||||
|
Image: "https://static.deepl.com/img/logo/DeepL_Logo_darkBlue_v2.svg",
|
||||||
|
Link: "https://www.deepl.com/translator",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "WaniKani",
|
||||||
|
Image: "https://assets.wanikani.com/assets/logo--retro-colors-b79775af8773b5a416e4ec3fae02e62d391b7e88e57f9fe0c2e4997a3383b002.png",
|
||||||
|
Link: "https://www.wanikani.com/",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Passwörter",
|
||||||
|
Image: "https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/resources/vaultwarden-logo-white.svg",
|
||||||
|
Link: "https://pw.tordarus.net",
|
||||||
|
Color: "#175ddc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Projekte",
|
||||||
|
Image: "https://git.tordarus.net/assets/img/logo.svg",
|
||||||
|
Link: "https://git.tordarus.net",
|
||||||
|
Color: "#383c4a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Wikipedia",
|
||||||
|
Image: "https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg",
|
||||||
|
Link: "https://www.wikipedia.org/",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Reddit",
|
||||||
|
Image: "https://www.redditinc.com/assets/images/site/reddit-logo.png",
|
||||||
|
Link: "https://www.reddit.com/",
|
||||||
|
Color: "#ff4300",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Tagesschau",
|
||||||
|
Image: "https://m47ch.com/wp-content/uploads/2010/12/Tagesschau.png",
|
||||||
|
Link: "https://www.golem.de/",
|
||||||
|
Color: "#003f8c",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Golem",
|
||||||
|
Image: "https://www.golem.de/staticrl/images/golem-logo-opt2.svg",
|
||||||
|
Link: "https://www.golem.de/",
|
||||||
|
Color: "#000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Comico",
|
||||||
|
Image: "https://play-lh.googleusercontent.com/xZ5bfg-XfyEmM1641bXduE27w44OmyWeVspg626wRYF0ejAgZAvsRKP4sECblfmnpg=w512",
|
||||||
|
ImageSize: "cover",
|
||||||
|
IconPadding: "0em",
|
||||||
|
Link: "https://comico.jp/",
|
||||||
|
Color: "#f40000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Manganelo",
|
||||||
|
Image: "https://manganato.com/themes/hm/images/logo.png",
|
||||||
|
Link: "https://manganato.com/",
|
||||||
|
Color: "#ffffff",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bookmark struct {
|
||||||
|
Title string
|
||||||
|
Image string
|
||||||
|
ImageSize string
|
||||||
|
IconPadding string
|
||||||
|
Color string
|
||||||
|
Link string
|
||||||
|
HideBorder bool
|
||||||
|
Order int
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseBookmarks() {
|
||||||
|
// set default values
|
||||||
|
for i := range Bookmarks {
|
||||||
|
if Bookmarks[i].Color == "" {
|
||||||
|
Bookmarks[i].Color = DefaultSettings.Background
|
||||||
|
}
|
||||||
|
if Bookmarks[i].ImageSize == "" {
|
||||||
|
Bookmarks[i].ImageSize = "contain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(Bookmarks, func(i, j int) bool {
|
||||||
|
return Bookmarks[i].Order < Bookmarks[j].Order
|
||||||
|
})
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module git.tordarus.net/Tordarus/homepage
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require golang.org/x/text v0.3.7 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
96
main.go
Normal file
96
main.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
intf = flag.String("i", "localhost", "interface")
|
||||||
|
port = flag.Uint("p", 80, "port")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed templates/*
|
||||||
|
TemplateFS embed.FS
|
||||||
|
|
||||||
|
//go:embed static/*
|
||||||
|
StaticFS embed.FS
|
||||||
|
|
||||||
|
Templates *template.Template
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ParseBookmarks()
|
||||||
|
|
||||||
|
if tmpl, err := template.New("homepage").ParseFS(TemplateFS, "templates/*"); err == nil {
|
||||||
|
Templates = tmpl
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
http.HandleFunc("/search", search)
|
||||||
|
http.HandleFunc("/style.css", style)
|
||||||
|
http.HandleFunc("/icons.ttf", icons)
|
||||||
|
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", *intf, *port), nil); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data := &TmplData{
|
||||||
|
text: GetText(r),
|
||||||
|
Bookmarks: Bookmarks,
|
||||||
|
Settings: DefaultSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Templates.ExecuteTemplate(w, "index.html", data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func style(w http.ResponseWriter, r *http.Request) {
|
||||||
|
file, err := TemplateFS.Open("templates/style.css")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "text/css")
|
||||||
|
|
||||||
|
if err := Templates.ExecuteTemplate(w, "style.css", DefaultSettings); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func icons(w http.ResponseWriter, r *http.Request) {
|
||||||
|
file, err := StaticFS.Open("static/icons.ttf")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "font/ttf")
|
||||||
|
|
||||||
|
io.Copy(w, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.ParseForm()
|
||||||
|
query := r.Form.Get("query")
|
||||||
|
w.Header().Add("Location", fmt.Sprintf("https://duckduckgo.com/?q=%s", query))
|
||||||
|
w.WriteHeader(http.StatusMovedPermanently)
|
||||||
|
}
|
19
settings.go
Normal file
19
settings.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Background string
|
||||||
|
IconMargin string
|
||||||
|
IconPadding string
|
||||||
|
IconWidth string
|
||||||
|
IconHeight string
|
||||||
|
BorderRadius string
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultSettings = &Settings{
|
||||||
|
Background: "#333",
|
||||||
|
IconMargin: "0.5em",
|
||||||
|
IconPadding: "1em",
|
||||||
|
IconWidth: "10em",
|
||||||
|
IconHeight: "10em",
|
||||||
|
BorderRadius: "0.15em",
|
||||||
|
}
|
BIN
static/icons.ttf
Normal file
BIN
static/icons.ttf
Normal file
Binary file not shown.
3
templates/bookmark.html
Normal file
3
templates/bookmark.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<a href="{{ .Link }}" class="bookmark" style='background-color: {{ .Color }}; {{ if not .HideBorder }} box-shadow: 0px 0px 3px black; {{ end }}; {{ if not (eq .IconPadding "") }} padding: {{ .IconPadding }}; {{ end }}'>
|
||||||
|
<div class="background" style='background-image: url("{{ .Image }}"); background-size: {{ .ImageSize }}'></div>
|
||||||
|
</a>
|
29
templates/index.html
Normal file
29
templates/index.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ .Language }}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ .Translate "Homepage" }}</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<section id="search">
|
||||||
|
<form method="post" action="/search">
|
||||||
|
<input type="search" id="query" placeholder='{{ .Translate "Search" }}' name="query"><label title="search" for="submit"><span class="material-icons">search</span></label>
|
||||||
|
<input id="submit" type="submit" title="search">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="bookmark-container">
|
||||||
|
{{ range .Bookmarks }}
|
||||||
|
{{ template "bookmark.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
146
templates/style.css
Normal file
146
templates/style.css
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("/icons.ttf");
|
||||||
|
src: local('Material Icons'),
|
||||||
|
local('MaterialIcons-Regular'),
|
||||||
|
url("/icons.ttf") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
transform: translateY(3px);
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px; /* Preferred icon size */
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
direction: ltr;
|
||||||
|
|
||||||
|
/* Support for all WebKit browsers. */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
/* Support for Safari and Chrome. */
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
|
||||||
|
/* Support for Firefox. */
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
|
/* Support for IE. */
|
||||||
|
font-feature-settings: 'liga';
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: {{ .Background }};
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bookmark-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search {
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form {
|
||||||
|
--input-font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form input[type="search"] {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid black;
|
||||||
|
outline: 0;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
border-radius: 0.25em 0 0 0.25em;
|
||||||
|
font-size: var(--input-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form input[type="search"]:focus {
|
||||||
|
box-shadow: 0px 0px 3px gray;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form input[type="search"]:focus + label[for="submit"] {
|
||||||
|
background-color: #111;
|
||||||
|
box-shadow: 0px 0px 3px gray;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form label[for="submit"] {
|
||||||
|
margin-left: 0;
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
border-radius: 0 0.25em 0.25em 0;
|
||||||
|
font-size: var(--input-font-size);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form label[for="submit"] img {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search form input[type="submit"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
width: {{ .IconWidth }};
|
||||||
|
height: {{ .IconHeight }};
|
||||||
|
margin: {{ .IconMargin }};
|
||||||
|
padding: {{ .IconPadding }};
|
||||||
|
border-radius: {{ .BorderRadius }};
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark:hover {
|
||||||
|
transform: scale(1.1, 1.1);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
60
text.go
Normal file
60
text.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Text struct {
|
||||||
|
Language string
|
||||||
|
Strings map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) Translate(str string) string {
|
||||||
|
if tr, ok := t.Strings[str]; ok {
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
Languages = map[language.Tag]Text{
|
||||||
|
language.English: {
|
||||||
|
Strings: map[string]string{
|
||||||
|
"Homepage": "Homepage",
|
||||||
|
"Search": "Search",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
language.German: {
|
||||||
|
Strings: map[string]string{
|
||||||
|
"Homepage": "Homepage",
|
||||||
|
"Search": "Suche",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
language.Japanese: {
|
||||||
|
Strings: map[string]string{
|
||||||
|
"Homepage": "ホームページ",
|
||||||
|
"Search": "検索",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var languageMatcher = language.NewMatcher([]language.Tag{
|
||||||
|
language.English,
|
||||||
|
language.German,
|
||||||
|
language.Japanese,
|
||||||
|
})
|
||||||
|
|
||||||
|
func GetText(r *http.Request) *Text {
|
||||||
|
fmt.Println(r.Header.Get("Accept-Language"))
|
||||||
|
lang, _ := language.MatchStrings(languageMatcher, r.Header.Get("Accept-Language"))
|
||||||
|
text := Languages[lang]
|
||||||
|
base, _ := lang.Base()
|
||||||
|
text.Language = base.String()
|
||||||
|
return &text
|
||||||
|
}
|
15
tmpl_data.go
Normal file
15
tmpl_data.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type TmplData struct {
|
||||||
|
text *Text
|
||||||
|
Bookmarks []Bookmark
|
||||||
|
Settings *Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TmplData) Translate(str string) string {
|
||||||
|
return d.text.Translate(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TmplData) Language() string {
|
||||||
|
return d.text.Language
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user