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