refactor code
This commit is contained in:
parent
670602a7bd
commit
e76a47f98a
8 changed files with 253 additions and 186 deletions
185
src/main.go
185
src/main.go
|
@ -1,51 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
docker "github.com/help-14/magma/addons/docker"
|
||||
healthcheckserver "github.com/help-14/magma/addons/health-check-server"
|
||||
"github.com/help-14/magma/modules"
|
||||
)
|
||||
|
||||
var pwd string
|
||||
var themeDir string
|
||||
var clientAddress string
|
||||
var appConfig modules.Config
|
||||
var websiteData = struct {
|
||||
Config modules.WebsiteConfig
|
||||
Language modules.Language
|
||||
Contents []modules.GroupData
|
||||
}{}
|
||||
var webTemplate *template.Template
|
||||
|
||||
func main() {
|
||||
prepare()
|
||||
loadData()
|
||||
go watchChanges()
|
||||
|
||||
commonfs := http.FileServer(http.Dir(filepath.Join(pwd, "data")))
|
||||
http.Handle("/common/", http.StripPrefix("/common/", commonfs))
|
||||
|
||||
languagefs := http.FileServer(http.Dir(filepath.Join(pwd, "languages")))
|
||||
http.Handle("/languages/", http.StripPrefix("/languages/", languagefs))
|
||||
|
||||
th := themeHandler{}
|
||||
http.Handle("/theme/", th)
|
||||
|
||||
http.HandleFunc("/weather", serveWeather)
|
||||
http.HandleFunc("/", serveTemplate)
|
||||
|
||||
modules.SetupLanguage()
|
||||
modules.SetupTemplate()
|
||||
modules.SetupWeather()
|
||||
//loadAddons()
|
||||
|
||||
log.Println("Listening on http://localhost:7001 ...")
|
||||
|
@ -56,75 +27,19 @@ func main() {
|
|||
}
|
||||
|
||||
func prepare() {
|
||||
pwd, _ = os.Getwd()
|
||||
|
||||
dataPath := filepath.Join(pwd, "data")
|
||||
dataPath := filepath.Join(modules.CurrentPath(), "data")
|
||||
os.MkdirAll(dataPath, os.ModePerm)
|
||||
|
||||
iconPath := filepath.Join(dataPath, "icon")
|
||||
os.RemoveAll(iconPath)
|
||||
os.MkdirAll(iconPath, os.ModePerm)
|
||||
|
||||
modules.CopyDir(filepath.Join(pwd, "common"), dataPath, false)
|
||||
}
|
||||
|
||||
func RemoveIndex(s []modules.BookmarkData, index int) {
|
||||
copy(s[index:], s[index+1:])
|
||||
s[len(s)-1] = modules.BookmarkData{"", "", "", false}
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
|
||||
func pruneData() {
|
||||
// Remove local ressources access
|
||||
for group := 0; group < len(websiteData.Contents); group++ {
|
||||
for col := 0; col < len(websiteData.Contents[group].Columns); col++ {
|
||||
bookmarks := websiteData.Contents[group].Columns[col].Bookmarks
|
||||
for bookmark := 0; bookmark < len(websiteData.Contents[group].Columns[col].Bookmarks); bookmark++ {
|
||||
bookmarkData := websiteData.Contents[group].Columns[col].Bookmarks[bookmark]
|
||||
if bookmarkData.IsLocal {
|
||||
RemoveIndex(bookmarks, bookmark)
|
||||
bookmark--
|
||||
}
|
||||
}
|
||||
websiteData.Contents[group].Columns[col].Bookmarks = bookmarks
|
||||
}
|
||||
}
|
||||
loadTemplate()
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
appConfig = modules.LoadConfig()
|
||||
websiteData.Config = appConfig.Website
|
||||
websiteData.Language = modules.LoadLanguage(appConfig.Website.Language)
|
||||
websiteData.Contents = modules.LoadContent().Data
|
||||
|
||||
// Download icon to local and remove local ressources access
|
||||
for group := 0; group < len(websiteData.Contents); group++ {
|
||||
for col := 0; col < len(websiteData.Contents[group].Columns); col++ {
|
||||
for bookmark := 0; bookmark < len(websiteData.Contents[group].Columns[col].Bookmarks); bookmark++ {
|
||||
bookmarkData := websiteData.Contents[group].Columns[col].Bookmarks[bookmark]
|
||||
if bookmarkData.IsImage() || bookmarkData.IsSVG() {
|
||||
iconPath := bookmarkData.Icon
|
||||
fileName := path.Base(iconPath)
|
||||
if modules.DownloadFile(iconPath, filepath.Join(pwd, "data", "icon", fileName)) {
|
||||
websiteData.Contents[group].Columns[col].Bookmarks[bookmark].Icon = "/common/icon/" + fileName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadTemplate()
|
||||
}
|
||||
|
||||
func loadTemplate() {
|
||||
themeDir = filepath.Join(pwd, "themes", appConfig.Website.Theme)
|
||||
tmpl, _ := template.ParseFiles(filepath.Join(themeDir, "index.html"))
|
||||
webTemplate = tmpl
|
||||
modules.CopyDir(filepath.Join(modules.CurrentPath(), "common"), dataPath, false)
|
||||
}
|
||||
|
||||
func loadAddons() {
|
||||
for i := 0; i < len(appConfig.Addons); i++ {
|
||||
switch addonName := appConfig.Addons[i]; addonName {
|
||||
for i := 0; i < len(modules.AppConfig.Addons); i++ {
|
||||
switch addonName := modules.AppConfig.Addons[i]; addonName {
|
||||
case "docker":
|
||||
docker.Setup()
|
||||
case "health-check-server":
|
||||
|
@ -132,89 +47,3 @@ func loadAddons() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func watchChanges() {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("Modified file:", event.Name)
|
||||
loadData()
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
watcher.Add(filepath.Join(pwd, "data", "data.yaml"))
|
||||
watcher.Add(filepath.Join(pwd, "data", "config.yaml"))
|
||||
watcher.Add(filepath.Join(pwd, "themes", appConfig.Website.Theme, "index.html"))
|
||||
<-done
|
||||
}
|
||||
|
||||
func ClientIsLocal(r *http.Request) bool {
|
||||
IPAddress := net.ParseIP(r.Header.Get("X-Real-Ip"))
|
||||
if IPAddress == nil {
|
||||
IPAddress = net.ParseIP(r.Header.Get("X-Forwarded-For"))
|
||||
}
|
||||
if IPAddress == nil {
|
||||
IPAddress = net.ParseIP(strings.Split(r.RemoteAddr, ":")[0])
|
||||
}
|
||||
return IPAddress.IsPrivate()
|
||||
}
|
||||
|
||||
func serveTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
if ! ClientIsLocal(r) {
|
||||
pruneData()
|
||||
} else {
|
||||
loadData()
|
||||
}
|
||||
webTemplate.Execute(w, websiteData)
|
||||
}
|
||||
|
||||
type themeHandler struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func (th themeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, strings.Replace(r.URL.Path, "/theme", themeDir, 1))
|
||||
}
|
||||
|
||||
var weatherTimeOut int64
|
||||
var weatherCache []byte
|
||||
|
||||
func serveWeather(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if appConfig.OpenWeatherMap.ApiKey == "demo" {
|
||||
w.Write([]byte("{\"coord\":{\"lon\":105.8085,\"lat\":21.0427},\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"base\":\"stations\",\"main\":{\"temp\":301.14,\"feels_like\":305.69,\"temp_min\":301.14,\"temp_max\":301.14,\"pressure\":1004,\"humidity\":83},\"visibility\":10000,\"wind\":{\"speed\":6.17,\"deg\":120},\"clouds\":{\"all\":75},\"dt\":1650981392,\"sys\":{\"type\":1,\"id\":9308,\"country\":\"VN\",\"sunrise\":1650925786,\"sunset\":1650971952},\"timezone\":25200,\"id\":1581130,\"name\":\"Hanoi\",\"cod\":200}"))
|
||||
} else {
|
||||
if time.Now().UnixMilli() >= weatherTimeOut {
|
||||
resp, err := http.Get("https://api.openweathermap.org/data/2.5/weather?lat=" + appConfig.OpenWeatherMap.Latitude + "&lon=" + appConfig.OpenWeatherMap.Longitude + "&limit=1&appid=" + appConfig.OpenWeatherMap.ApiKey)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return
|
||||
}
|
||||
weatherCache = body
|
||||
weatherTimeOut = time.Now().UnixMilli() + 1800000
|
||||
}
|
||||
w.Write(weatherCache)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ type OpenWeatherMapConfig struct {
|
|||
Latitude string `yaml:"lat"`
|
||||
}
|
||||
|
||||
func LoadConfig() Config {
|
||||
var AppConfig Config
|
||||
|
||||
func LoadConfig() {
|
||||
defaultConfig := Config{
|
||||
Website: WebsiteConfig{
|
||||
Title: "Magma Dashboard",
|
||||
|
@ -41,20 +43,21 @@ func LoadConfig() Config {
|
|||
},
|
||||
Addons: []string{},
|
||||
}
|
||||
AppConfig = defaultConfig
|
||||
|
||||
yamlFile, err := ioutil.ReadFile(filepath.Join("data", "config.yaml"))
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading YAML file: %s\n", err)
|
||||
return defaultConfig
|
||||
return
|
||||
}
|
||||
|
||||
var yamlConfig Config
|
||||
err = yaml.Unmarshal(yamlFile, &yamlConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing YAML file: %s\n", err)
|
||||
return defaultConfig
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Loaded config:", yamlConfig)
|
||||
return yamlConfig
|
||||
AppConfig = yamlConfig
|
||||
}
|
||||
|
|
|
@ -28,10 +28,11 @@ type ColumnData struct {
|
|||
}
|
||||
|
||||
type BookmarkData struct {
|
||||
Name string `yaml:"name"`
|
||||
Url string `yaml:"url"`
|
||||
Icon string `yaml:"icon"`
|
||||
IsLocal bool `yaml:"isLocal"`
|
||||
Name string `yaml:"name"`
|
||||
Url string `yaml:"url"`
|
||||
UrlLocal string `yaml:"urlLocal"`
|
||||
Icon string `yaml:"icon"`
|
||||
IsLocal bool `yaml:"isLocal"`
|
||||
}
|
||||
|
||||
func LoadContent() ContentData {
|
||||
|
|
|
@ -7,6 +7,15 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
var pwd string
|
||||
|
||||
func CurrentPath() string {
|
||||
if len(pwd) <= 0 {
|
||||
pwd, _ = os.Getwd()
|
||||
}
|
||||
return pwd
|
||||
}
|
||||
|
||||
func Exists(path string) bool {
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return true
|
||||
|
|
|
@ -3,11 +3,17 @@ package modules
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func SetupLanguage() {
|
||||
languagefs := http.FileServer(http.Dir(filepath.Join(CurrentPath(), "languages")))
|
||||
http.Handle("/languages/", http.StripPrefix("/languages/", languagefs))
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
Greeting LanguageGreeting `yaml:"greeting"`
|
||||
Weather LanguageWeather `yaml:"weather"`
|
||||
|
|
18
src/modules/network.go
Normal file
18
src/modules/network.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ClientIsLocal(r *http.Request) bool {
|
||||
IPAddress := net.ParseIP(r.Header.Get("X-Real-Ip"))
|
||||
if IPAddress == nil {
|
||||
IPAddress = net.ParseIP(r.Header.Get("X-Forwarded-For"))
|
||||
}
|
||||
if IPAddress == nil {
|
||||
IPAddress = net.ParseIP(strings.Split(r.RemoteAddr, ":")[0])
|
||||
}
|
||||
return IPAddress.IsPrivate()
|
||||
}
|
155
src/modules/template.go
Normal file
155
src/modules/template.go
Normal file
|
@ -0,0 +1,155 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func SetupTemplate() {
|
||||
loadData()
|
||||
go watchChanges()
|
||||
|
||||
commonfs := http.FileServer(http.Dir(filepath.Join(pwd, "data")))
|
||||
http.Handle("/common/", http.StripPrefix("/common/", commonfs))
|
||||
|
||||
th := themeHandler{}
|
||||
http.Handle("/theme/", th)
|
||||
|
||||
http.HandleFunc("/", serveTemplate)
|
||||
}
|
||||
|
||||
var websiteData = struct {
|
||||
Config WebsiteConfig
|
||||
Language Language
|
||||
Contents []GroupData
|
||||
IsLocal bool
|
||||
}{}
|
||||
var privateContent []GroupData
|
||||
var publicContent []GroupData
|
||||
var webTemplate *template.Template
|
||||
|
||||
func loadData() {
|
||||
LoadConfig()
|
||||
websiteData.Config = AppConfig.Website
|
||||
websiteData.Language = LoadLanguage(AppConfig.Website.Language)
|
||||
websiteData.Contents = LoadContent().Data
|
||||
|
||||
// Download icon to local and remove local ressources access
|
||||
for groupIndex := 0; groupIndex < len(websiteData.Contents); groupIndex++ {
|
||||
group := websiteData.Contents[groupIndex]
|
||||
groupDataPublic := []ColumnData{}
|
||||
groupDataPrivate := []ColumnData{}
|
||||
|
||||
for colIndex := 0; colIndex < len(group.Columns); colIndex++ {
|
||||
column := group.Columns[colIndex]
|
||||
columnDataPublic := []BookmarkData{}
|
||||
columnDataPrivate := []BookmarkData{}
|
||||
|
||||
for bookmarkIndex := 0; bookmarkIndex < len(column.Bookmarks); bookmarkIndex++ {
|
||||
bookmarkData := column.Bookmarks[bookmarkIndex]
|
||||
iconPath := bookmarkData.Icon
|
||||
|
||||
//download icon
|
||||
if bookmarkData.IsImage() || bookmarkData.IsSVG() {
|
||||
fileName := path.Base(iconPath)
|
||||
if DownloadFile(iconPath, filepath.Join(pwd, "data", "icon", fileName)) {
|
||||
iconPath = "/common/icon/" + fileName
|
||||
}
|
||||
}
|
||||
|
||||
//add to private array
|
||||
if bookmarkData.IsLocal || len(bookmarkData.UrlLocal) > 0 {
|
||||
url := bookmarkData.Url
|
||||
if len(bookmarkData.UrlLocal) > 0 {
|
||||
url = bookmarkData.UrlLocal
|
||||
}
|
||||
columnDataPrivate = append(columnDataPrivate, BookmarkData{bookmarkData.Name, url, bookmarkData.UrlLocal, iconPath, true})
|
||||
}
|
||||
|
||||
//add to public array
|
||||
if !bookmarkData.IsLocal && len(bookmarkData.Url) > 0 {
|
||||
columnDataPublic = append(columnDataPublic, BookmarkData{bookmarkData.Name, bookmarkData.Url, bookmarkData.UrlLocal, iconPath, false})
|
||||
}
|
||||
}
|
||||
|
||||
if len(columnDataPublic) > 0 {
|
||||
groupDataPublic = append(groupDataPublic, ColumnData{column.Title, columnDataPublic, column.Icon})
|
||||
}
|
||||
if len(columnDataPrivate) > 0 {
|
||||
groupDataPrivate = append(groupDataPrivate, ColumnData{column.Title, columnDataPrivate, column.Icon})
|
||||
}
|
||||
}
|
||||
|
||||
if len(groupDataPublic) > 0 {
|
||||
publicContent = append(publicContent, GroupData{group.Title, groupDataPublic, group.Icon})
|
||||
}
|
||||
if len(groupDataPrivate) > 0 {
|
||||
privateContent = append(privateContent, GroupData{group.Title, groupDataPrivate, group.Icon})
|
||||
}
|
||||
}
|
||||
|
||||
loadTemplate()
|
||||
}
|
||||
|
||||
var themeDir string
|
||||
|
||||
type themeHandler struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func (th themeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, strings.Replace(r.URL.Path, "/theme", themeDir, 1))
|
||||
}
|
||||
|
||||
func loadTemplate() {
|
||||
themeDir = filepath.Join(pwd, "themes", AppConfig.Website.Theme)
|
||||
tmpl, _ := template.ParseFiles(filepath.Join(themeDir, "index.html"))
|
||||
webTemplate = tmpl
|
||||
}
|
||||
|
||||
func serveTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
if ClientIsLocal(r) {
|
||||
websiteData.Contents = privateContent
|
||||
} else {
|
||||
websiteData.Contents = publicContent
|
||||
}
|
||||
webTemplate.Execute(w, websiteData)
|
||||
}
|
||||
|
||||
func watchChanges() {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("Modified file:", event.Name)
|
||||
loadData()
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
watcher.Add(filepath.Join(CurrentPath(), "data", "data.yaml"))
|
||||
watcher.Add(filepath.Join(CurrentPath(), "data", "config.yaml"))
|
||||
watcher.Add(filepath.Join(CurrentPath(), "themes", AppConfig.Website.Theme, "index.html"))
|
||||
<-done
|
||||
}
|
46
src/modules/weather.go
Normal file
46
src/modules/weather.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SetupWeather() {
|
||||
http.HandleFunc("/weather", serveWeather)
|
||||
}
|
||||
|
||||
func serveWeather(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
data := GetWeather(AppConfig.OpenWeatherMap.ApiKey, AppConfig.OpenWeatherMap.Latitude, AppConfig.OpenWeatherMap.Longitude)
|
||||
if data != nil {
|
||||
w.Write(data)
|
||||
}
|
||||
}
|
||||
|
||||
var weatherTimeOut int64
|
||||
var weatherCache []byte
|
||||
var demoData = []byte("{\"coord\":{\"lon\":105.8085,\"lat\":21.0427},\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"base\":\"stations\",\"main\":{\"temp\":301.14,\"feels_like\":305.69,\"temp_min\":301.14,\"temp_max\":301.14,\"pressure\":1004,\"humidity\":83},\"visibility\":10000,\"wind\":{\"speed\":6.17,\"deg\":120},\"clouds\":{\"all\":75},\"dt\":1650981392,\"sys\":{\"type\":1,\"id\":9308,\"country\":\"VN\",\"sunrise\":1650925786,\"sunset\":1650971952},\"timezone\":25200,\"id\":1581130,\"name\":\"Hanoi\",\"cod\":200}")
|
||||
|
||||
func GetWeather(apiKey string, latitude string, longitude string) []byte {
|
||||
if apiKey == "demo" {
|
||||
return demoData
|
||||
} else {
|
||||
if time.Now().UnixMilli() >= weatherTimeOut {
|
||||
resp, err := http.Get("https://api.openweathermap.org/data/2.5/weather?lat=" + latitude + "&lon=" + longitude + "&limit=1&appid=" + apiKey)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return nil
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return nil
|
||||
}
|
||||
weatherCache = body
|
||||
weatherTimeOut = time.Now().UnixMilli() + 1800000
|
||||
}
|
||||
return weatherCache
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue