running ok now

This commit is contained in:
Help-14 2022-04-09 22:41:37 +07:00
parent 21f3b35db1
commit 8e4a4f5cba
18 changed files with 297 additions and 324 deletions

3
.gitignore vendored
View file

@ -104,4 +104,5 @@ dist
.tern-port
src/temp
src/build
src/build
src/common/*.yaml

View file

@ -1,4 +1,3 @@
function loadCSS() {
for (let c = 0; c < document.styleSheets.length; c++) {
var declaration = document.styleSheets[c].cssRules[0];
@ -14,4 +13,5 @@ function loadCSS() {
return;
}
}
}
}
loadCSS();

View file

@ -1,8 +0,0 @@
{
"greeting": {
"morning": "Good morning!",
"afternoon": "Good afternoon!",
"evening": "Good evening!",
"night": "Good night!"
}
}

5
src/languages/en.yaml Normal file
View file

@ -0,0 +1,5 @@
greeting:
morning: Good morning!
afternoon: Good afternoon!
evening: Good evening!
night: Good night!

View file

@ -1,8 +0,0 @@
{
"greeting": {
"morning": "Chào buổi sáng!",
"afternoon": "Chiều làm việc hiệu quả!",
"evening": "Chiều tối vui vẻ!",
"night": "Chúc ngủ ngon!"
}
}

5
src/languages/vi.yaml Normal file
View file

@ -0,0 +1,5 @@
greeting:
morning: Chào buổi sáng!
afternoon: Chiều làm việc hiệu quả!
evening: Chiều tối vui vẻ!
night: Chúc ngủ ngon!

View file

@ -1,19 +1,33 @@
package main
import (
"errors"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"time"
"github.com/help-14/magma/modules"
)
var appConfig modules.Config
var websiteData = struct {
Config modules.WebsiteConfig
Language modules.Language
Contents []modules.GroupData
}{}
func main() {
prepareSampleFiles()
appConfig = modules.LoadConfig()
websiteData.Config = appConfig.Website
websiteData.Language = modules.LoadLanguage(appConfig.Website.Language)
websiteData.Contents = modules.LoadContent().Data
commonfs := http.FileServer(http.Dir("./common"))
http.Handle("/common/", http.StripPrefix("/common/", commonfs))
@ -24,6 +38,7 @@ func main() {
themefs := http.FileServer(http.Dir("." + themePath))
http.Handle("/theme/", http.StripPrefix("/theme/", themefs))
http.HandleFunc("/weather", serveWeather)
http.HandleFunc("/", serveTemplate)
log.Println("Listening on http://localhost:7001 ...")
@ -33,17 +48,45 @@ func main() {
}
}
var weatherTimeOut int64
var weatherCache []byte
func serveWeather(w http.ResponseWriter, r *http.Request) {
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.Header().Set("Content-Type", "application/json")
w.Write(weatherCache)
}
func serveTemplate(w http.ResponseWriter, r *http.Request) {
lp := filepath.Join("themes", appConfig.Website.Theme, "index.html")
tmpl, _ := template.ParseFiles(lp)
tmpl.Execute(w, struct {
Config modules.WebsiteConfig
}{appConfig.Website})
tmpl.Execute(w, websiteData)
}
// templ.Execute(file, struct {
// Age int
// Name string
// }{42, "Dolphin"})
// {{.Age}}, {{.Name}}
func prepareSampleFiles() {
files, err := ioutil.ReadDir("./sample/")
if err != nil {
log.Fatal(err)
return
}
for _, file := range files {
samplePath := filepath.Join("sample", file.Name())
commonPath := filepath.Join("common", file.Name())
if _, err := os.Stat(commonPath); errors.Is(err, os.ErrNotExist) {
modules.CopyFile(samplePath, commonPath)
}
}
}

47
src/modules/content.go Normal file
View file

@ -0,0 +1,47 @@
package modules
import (
"fmt"
"io/ioutil"
"path/filepath"
"gopkg.in/yaml.v2"
)
type ContentData struct {
Data []GroupData `yaml:"data"`
}
type GroupData struct {
Title string `yaml:"title"`
Columns []ColumnData `yaml:"columns"`
}
type ColumnData struct {
Title string `yaml:"title"`
Bookmarks []BookmarkData `yaml:"bookmarks"`
}
type BookmarkData struct {
Name string `yaml:"name"`
Icon string `yaml:"icon"`
Url string `yaml:"url"`
}
func LoadContent() ContentData {
emptyData := ContentData{}
yamlFile, err := ioutil.ReadFile(filepath.Join("common", "data.yaml"))
if err != nil {
fmt.Printf("Error reading YAML file: %s\n", err)
return emptyData
}
var data ContentData
err = yaml.Unmarshal(yamlFile, &data)
if err != nil {
fmt.Printf("Error parsing YAML file: %s\n", err)
return emptyData
}
return data
}

27
src/modules/file.go Normal file
View file

@ -0,0 +1,27 @@
package modules
import (
"io"
"log"
"os"
)
func CopyFile(src string, dst string) {
fin, err := os.Open(src)
if err != nil {
log.Fatal(err)
}
defer fin.Close()
fout, err := os.Create(dst)
if err != nil {
log.Fatal(err)
}
defer fout.Close()
_, err = io.Copy(fout, fin)
if err != nil {
log.Fatal(err)
}
}

38
src/modules/language.go Normal file
View file

@ -0,0 +1,38 @@
package modules
import (
"fmt"
"io/ioutil"
"path/filepath"
"gopkg.in/yaml.v2"
)
type Language struct {
Greeting LanguageGreeting `yaml:"greeting"`
}
type LanguageGreeting struct {
Morning string `yaml:"morning"`
Afternoon string `yaml:"afternoon"`
Evening string `yaml:"evening"`
Night string `yaml:"night"`
}
func LoadLanguage(language string) Language {
yamlFile, err := ioutil.ReadFile(filepath.Join("languages", language+".yaml"))
if err != nil {
fmt.Printf("Error reading YAML file: %s\n", err)
return LoadLanguage("en")
}
var data Language
err = yaml.Unmarshal(yamlFile, &data)
if err != nil {
fmt.Printf("Error parsing YAML file: %s\n", err)
return LoadLanguage("en")
}
fmt.Println("Loaded language: ", language)
return data
}

View file

@ -1,2 +0,0 @@
export class Language {
}

View file

@ -1,107 +0,0 @@
import { opine, serveStatic } from "https://deno.land/x/opine/mod.ts";
import { Language, minify } from "https://deno.land/x/minifier/mod.ts";
import {
ensureDirSync,
ensureFileSync,
existsSync,
walkSync,
} from "https://deno.land/std/fs/mod.ts";
const tempFolder = `${Deno.cwd()}/temp`;
let weatherCache = "";
let weatherTimeOut = new Date().getTime();
export async function startServer(): Promise<void> {
// Create web server
const app = opine();
// Add resource folder
app.use(serveStatic(tempFolder));
// Add route for weather
app.get("/weather", async (req, res) => {
if (new Date().getTime() >= weatherTimeOut) {
const lat = "21.0425886";
const lon = "105.8129389";
const apiKey = "7b093bee7461b669e34c363d887cfdec";
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&limit=1&appid=${apiKey}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
},
);
weatherCache = await response.json();
weatherTimeOut = new Date().getTime() + 1800000;
}
res.send(weatherCache);
});
// Minify all files and copy to temp folder
await Compile();
// Start web server
app.listen(7001, () =>
console.log(
`Server has started on http://localhost:7001 🚀`,
));
}
export async function Compile(): Promise<void> {
ensureDirSync(tempFolder);
[`private/themes/${window.config.website.theme}`, "public"].forEach(
(folder) => {
for (
const entry of walkSync(`${Deno.cwd()}/${folder}/`, {
includeDirs: false,
})
) {
console.log("Preparing: " + entry.path);
let language = null;
if (!entry.path.includes(".min.")) {
if (entry.path.includes(".css")) {
language = Language.CSS;
} else if (entry.path.includes(".json")) {
language = Language.JSON;
}
// minifier module make error when minify js and html file 🥲
//else if (entry.path.includes(".js")) {
// language = Language.JS;
//}
//else if (entry.path.includes(".htm")) {
//language = Language.HTML;
//}
}
const moveToPath = convertOutputPath(entry.path, folder);
ensureFileSync(moveToPath);
if (language) {
let content = Deno.readTextFileSync(entry.path);
content = minify(Language.HTML, content);
Deno.writeTextFileSync(moveToPath, content);
} else {
Deno.copyFileSync(entry.path, moveToPath);
}
}
},
);
// Copy language
let languagePath =
`${Deno.cwd()}/public/languages/${window.config.website.language}.json`;
const languageOutput = convertOutputPath(languagePath, "public/languages");
if (!existsSync(languagePath)) {
languagePath =
`${Deno.cwd()}/private/languages/${window.config.website.language}.json`;
}
Deno.copyFileSync(languagePath, languageOutput);
}
function convertOutputPath(path: string, from: string) {
return Deno.cwd() + path.replace(Deno.cwd(), "").replace(from, "temp");
}

View file

@ -1,9 +1,12 @@
website:
theme: "flame"
title: "Help-14 Dashboard"
title: "Magma Dashboard"
description: ""
language: "vi"
localization: "vi-vn"
language: "en"
localization: "en-US"
useMetric: true
addons:
openweathermap:
apiKey:
lon:
lat:
addons:

View file

@ -21,7 +21,7 @@ data:
icon: fa-solid fa-server
url: https://www.truenas.com
- name: Kerberos
icon: fa-solid fa-camera-web
icon: fa-solid fa-video
url: https://kerberos.io
- name: jDownloader
icon: fa-solid fa-cloud-arrow-down
@ -61,10 +61,10 @@ data:
icon: fa-solid fa-bag-shopping
url: https://shopee.vn
- name: Lazada
icon: fa-solid fa-heart-half-stroke
url: https://www.lazada.com
icon: fa-brands fa-amazon
url: https://www.amazon.com
- title: Tools
bookmarks:
- name: Maps
icon: fa-solid fa-map
url: https://www.google.com/maps
url: https://www.google.com/maps

View file

@ -68,20 +68,29 @@ body {
font-weight: bold;
}
.group-items .icon {
.group-items .icon-container {
margin-top: 0px !important;
margin-left: 0.5em !important;
margin-right: 0.5em !important;
text-align: center;
width: 32px;
height: 32px;
align-items: center;
}
.group-items .icon {
vertical-align: middle;
}
.group-items a:hover {
background-color: rgba(0, 0, 0, 0.3) !important;
cursor: pointer;
}
@supports (-webkit-backdrop-filter: none) or (backdrop-filter: none) {
.group-items a:hover {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background-color: rgba(0, 0, 0, 0.3) !important;
}
}
}

View file

@ -41,22 +41,108 @@
</div>
</div>
<div class="row">
<div id="content" class="column twelve"></div>
<div id="content" class="column twelve">
{{range .Contents}}
<div class="row group-title"><h4 class="strong">{{.Title}}</h4></div>
<div class="row">
{{range .Columns}}
<div class="three columns group-items">
<h6 class="accent">{{.Title}}</h6>
{{range .Bookmarks}}
<a href="{{.Url}}">
<div class="icon-container">
<i class="{{.Icon}} fa-xl icon"></i>
</div>
<h6>{{.Name}}</h6>
</a>
{{end}}
</div>
{{end}}
</div>
{{end}}
</div>
</div>
<div id="footer" class="row"></div>
</div>
<script>
window.config = {
localization: {{ .Config.Localization }},
language: {{ .Config.Language }},
useMetric: {{ .Config.UseMetric }}
};
</script>
<script src="theme/js/skycons.min.js"></script>
<script src="common/js/core.js"></script>
<script src="theme/js/magma.js"></script>
<script>
window.config = {
localization: {{.Config.Localization}},
language: {{.Config.Language}},
useMetric: {{.Config.UseMetric}}
};
(function setTimer() {
const clock = document.querySelector("#clock");
const clockOptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
(function clockTick() {
clock.innerText = new Date().toLocaleTimeString(window.config.localization, clockOptions);
setTimeout(clockTick, 2000);
})();
//Set greeting
const greeting = document.querySelector("#greeting");
const hour = new Date().getHours();
if (hour >= 5 && hour < 12) {
greeting.innerText = {{.Language.Greeting.Morning}};
}
else if (hour >= 12 && hour < 17) {
greeting.innerText = {{.Language.Greeting.Afternoon}};
}
else if (hour >= 17 && hour < 20) {
greeting.innerText = {{.Language.Greeting.Evening}};
}
else {
greeting.innerText = {{.Language.Greeting.Night}};
}
})();
(async function loadWeather() {
// Get info from api
const weather = await (await fetch("./weather")).json();
// Parse weather id
let icon = null;
let isDay = Date.now().hour >= 6 && Date.now().hour < 18;
const weatherCode = weather.weather[0].id;
if ([200, 201, 202, 210, 211, 212, 221, 230, 231, 232].includes(weatherCode)) {
icon = Skycons.RAIN; //Thunderstorm
} else if ([300, 301, 302, 310, 311, 312, 313, 314, 321].includes(weatherCode)) {
icon = Skycons.RAIN; //Drizzle
} else if ([500, 501, 502, 503, 504, 511, 520, 521, 522, 531].includes(weatherCode)) {
icon = Skycons.RAIN;
} else if ([600, 601, 602, 611, 612, 613, 615, 616, 620, 621, 622].includes(weatherCode)) {
icon = Skycons.SNOW;
} else if (weatherCode === 800) {
icon = isDay ? Skycons.CLEAR_DAY : Skycons.CLEAR_NIGHT;
} else if ([801, 802, 803, 804].includes(weatherCode)) {
if (weatherCode >= 803) {
icon = Skycons.CLOUDY;
} else {
icon = isDay ? Skycons.PARTLY_CLOUDY_DAY : Skycons.PARTLY_CLOUDY_NIGHT;
}
} else if ([762, 761, 751, 731, 721].includes(weatherCode)) {
icon = Skycons.SLEET;
} else if ([771, 781].includes(weatherCode)) {
icon = Skycons.WIND;
} else if ([701, 711, 741].includes(weatherCode)) {
icon = Skycons.FOG;
} else {
return;
}
// Set weather icon to canvas
var skycons = new Skycons({ "color": window.cssRoot["--accentColor"] });
skycons.add("weather-icon", icon);
// Set weather info
if (window.config.useMetric) {
document.querySelector("#temp").innerText = Math.floor(weather.main.temp - 273.15) + "°C";
} else {
document.querySelector("#temp").innerText = Math.floor((weather.main.temp - 32) * 5 / 9) + "°F";
}
document.querySelector("#humidity").innerText = Math.floor(weather.main.humidity) + "%";
document.querySelector("#weather-info").style.visibility = "visible";
})();
</script>
<script src="common/js/custom.js"></script>
</body>

View file

@ -1,166 +0,0 @@
// async function loadConfig() {
// window.config = await (await fetch("./common/config.json")).json();
// // Set website language
// document.documentElement.setAttribute("lang", window.config.website.language);
// // Set website title
// document.title = window.config.website.title;
// // Set website description
// document.querySelector('meta[name="description"]').setAttribute("content", window.config.website.description);
// }
async function loadLanguage() {
try {
window.language = await (await fetch(`./languages/${window.config.language}.json`)).json();
} catch {
console.error("Language file not found");
window.language = await (await fetch(`./languages/en.json`)).json();
}
}
async function loadWeather() {
return;
// Get info from api
const weather = await (await fetch("./weather")).json();
// Parse weather id
let icon = null;
let isDay = Date.now().hour >= 6 && Date.now().hour < 18;
const weatherCode = weather.weather[0].id;
if ([200, 201, 202, 210, 211, 212, 221, 230, 231, 232].includes(weatherCode)) {
icon = Skycons.RAIN; //Thunderstorm
} else if ([300, 301, 302, 310, 311, 312, 313, 314, 321].includes(weatherCode)) {
icon = Skycons.RAIN; //Drizzle
} else if ([500, 501, 502, 503, 504, 511, 520, 521, 522, 531].includes(weatherCode)) {
icon = Skycons.RAIN;
} else if ([600, 601, 602, 611, 612, 613, 615, 616, 620, 621, 622].includes(weatherCode)) {
icon = Skycons.SNOW;
} else if (weatherCode === 800) {
icon = isDay ? Skycons.CLEAR_DAY : Skycons.CLEAR_NIGHT;
} else if ([801, 802, 803, 804].includes(weatherCode)) {
if (weatherCode >= 803) {
icon = Skycons.CLOUDY;
} else {
icon = isDay ? Skycons.PARTLY_CLOUDY_DAY : Skycons.PARTLY_CLOUDY_NIGHT;
}
} else if ([762, 761, 751, 731, 721].includes(weatherCode)) {
icon = Skycons.SLEET;
} else if ([771, 781].includes(weatherCode)) {
icon = Skycons.WIND;
} else if ([701, 711, 741].includes(weatherCode)) {
icon = Skycons.FOG;
} else {
return;
}
// Set weather icon to canvas
var skycons = new Skycons({ "color": window.cssRoot["--accentColor"] });
skycons.add("weather-icon", icon);
// Set weather info
if (window.config.useMetric) {
document.querySelector("#temp").innerText = Math.floor(weather.main.temp - 273.15) + "°C";
} else {
document.querySelector("#temp").innerText = Math.floor((weather.main.temp - 32) * 5 / 9) + "°F";
}
document.querySelector("#humidity").innerText = Math.floor(weather.main.humidity) + "%";
document.querySelector("#weather-info").style.visibility = "visible";
}
async function loadBookmarks() {
const data = await (await fetch("./common/data.json")).json();
let contentHtml = "";
function splitToChunks(arr) {
var chunks = [[], [], [], []];
var i = 0;
for (let j = 0; j < arr.length; j++) {
chunks[i].push(arr[j]);
i++;
if (i === 4) i = 0;
}
return chunks;
}
function splitToEqualChunks(arr) {
var chunks = [];
var i = 0;
while (i < arr.length) {
chunks.push(arr.slice(i, Math.min(i + 4, arr.length)));
i += 4;
}
return chunks;
}
data.forEach(section => {
contentHtml += `<div class="row group-title"><h4 class="strong">${section.title}</h4></div>`
if (section.columns) {
const chunks = splitToEqualChunks(section.columns)
chunks.forEach(chunk => {
contentHtml += '<div class="row">';
chunk.forEach(column => {
contentHtml += `
<div class="three columns group-items">
<h6 class="accent">${column.title}</h6>`;
column.bookmarks.forEach(bookmark => {
contentHtml += `
<a href="${bookmark.url}">
<i class="${bookmark.icon} fa-xl icon"></i>
<h6>${bookmark.title}</h6>
</a>`;
});
contentHtml += "</div>";
});
contentHtml += "</div>";
});
} else if (section.bookmarks) {
contentHtml += '<div class="row">';
const chunks = splitToChunks(section.bookmarks)
chunks.forEach(chunk => {
contentHtml += '<div class="three columns group-items">';
chunk.forEach(bookmark => {
contentHtml += `
<a href="${bookmark.url}">
<i class="${bookmark.icon} fa-xl icon"></i>
<h6>${bookmark.title}</h6>
</a>`;
});
contentHtml += "</div>";
});
contentHtml += "</div>";
}
});
document.querySelector("#content").innerHTML = contentHtml;
}
function setClock() {
//Set clock
const clock = document.querySelector("#clock");
const clockOptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
(function clockTick() {
clock.innerText = new Date().toLocaleTimeString(window.config.localization, clockOptions);
setTimeout(clockTick, 2000);
})();
//Set greeting
const greeting = document.querySelector("#greeting");
const hour = new Date().getHours();
if (hour >= 5 && hour < 12) {
greeting.innerText = window.language.greeting.morning;
}
else if (hour >= 12 && hour < 17) {
greeting.innerText = window.language.greeting.afternoon;
}
else if (hour >= 17 && hour < 20) {
greeting.innerText = window.language.greeting.evening;
}
else {
greeting.innerText = window.language.greeting.night;
}
}
async function startWebsite() {
loadBookmarks();
await loadLanguage();
setClock();
loadCSS();
loadWeather();
}
startWebsite();