Use random ID values to prevent ID conflicts

This commit is contained in:
Elara 2021-03-27 14:50:52 -07:00
parent c675ce8e26
commit 1f95f1e091
6 changed files with 63 additions and 39 deletions

View File

@ -35,7 +35,8 @@ A user can be added under the `[users]` section like so:
passwordHash = "$2a$10$w00dzQ1PP6nwXLhuzV2pFOUU6m8bcZXtDX3UVxpOYq3fTSwVMqPge" passwordHash = "$2a$10$w00dzQ1PP6nwXLhuzV2pFOUU6m8bcZXtDX3UVxpOYq3fTSwVMqPge"
showPublic = true showPublic = true
``` ```
`passwordHash` should be a bcrypt hash of the desired password with a cost of 10 (default) `passwordHash` should be a bcrypt hash of the desired password with a cost of 10 (default). `simpledash --hash <password>` can be used to get a suitable hash
`showPublic` should be a boolean denoting whether public cards should be displayed while signed in `showPublic` should be a boolean denoting whether public cards should be displayed while signed in
#### Cards #### Cards

13
main.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"github.com/Masterminds/sprig" "github.com/Masterminds/sprig"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -9,6 +10,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"github.com/wader/gormstore/v2" "github.com/wader/gormstore/v2"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
@ -39,9 +41,20 @@ func main() {
addr := flag.IPP("addr", "a", net.ParseIP("0.0.0.0"), "Bind address for HTTP server") addr := flag.IPP("addr", "a", net.ParseIP("0.0.0.0"), "Bind address for HTTP server")
port := flag.IntP("port", "p", 8080, "Bind port for HTTP server") port := flag.IntP("port", "p", 8080, "Bind port for HTTP server")
config := flag.StringP("config", "c", "simpledash.toml", "TOML config file") config := flag.StringP("config", "c", "simpledash.toml", "TOML config file")
hash := flag.String("hash", "", "Generate new bcrypt password hash")
flag.ErrHelp = errors.New("simpledash: help requested")
// Parse flags // Parse flags
flag.Parse() flag.Parse()
if *hash != "" {
hash, err := bcrypt.GenerateFromPassword([]byte(*hash), bcrypt.DefaultCost)
if err != nil {
Log.Fatal().Err(err).Msg("Error creating bcrypt hash")
}
fmt.Println(string(hash))
os.Exit(0)
}
// Create new router // Create new router
router := mux.NewRouter().StrictSlash(true) router := mux.NewRouter().StrictSlash(true)

View File

@ -1,5 +1,5 @@
{{- $format := splitList "\n" (trim .Data.format) -}} {{- $format := splitList "\n" (trim .Data.format) -}}
{{- $title := replace " " "" .Title -}} {{- $randID := randAlphaNum 10 -}}
<div class="card-header"> <div class="card-header">
<a class="card-header-title" href="{{.URL}}"> <a class="card-header-title" href="{{.URL}}">
{{if ne .Icon ""}} {{if ne .Icon ""}}
@ -9,10 +9,9 @@
</a> </a>
</div> </div>
<div class="card-content"> <div class="card-content">
<p id="{{$title}}LoadingText">Loading...</p> <p id="APILoadingText_{{$randID}}">Loading...</p>
{{range $_, $accessStr := $format}} {{range $index, $fmtStr := $format}}
{{- $id := printf `%s_%s` $title (b64enc $accessStr) -}} <div id="{{printf `APIElement%d_%s` $index $randID}}"></div>
<p id="{{$id}}"></p>
{{end}} {{end}}
</div> </div>
{{if .Data.footer}} {{if .Data.footer}}
@ -25,10 +24,9 @@
request.open('GET', "{{proxy .URL}}", true) request.open('GET', "{{proxy .URL}}", true)
request.onload = function () { request.onload = function () {
const data = JSON.parse(this.response) const data = JSON.parse(this.response)
document.getElementById("{{$title}}LoadingText").classList.add("is-hidden") document.getElementById("APILoadingText_{{$randID}}").classList.add("is-hidden")
{{range $_, $accessStr := $format}} {{range $index, $fmtStr := $format}}
{{- $id := printf `%s_%s` $title (b64enc $accessStr) -}} document.getElementById("{{printf `APIElement%d_%s` $index $randID}}").innerHTML = `{{unescJS (trim $fmtStr)}}`
document.getElementById("{{$id}}").innerHTML = `{{unescJS (trim $accessStr)}}`
{{end}} {{end}}
} }
request.send() request.send()

View File

@ -1,3 +1,4 @@
{{- $randID := randAlphaNum 10 -}}
<div class="card-header"> <div class="card-header">
<a class="card-header-title" href="{{.URL}}"> <a class="card-header-title" href="{{.URL}}">
{{if ne .Icon ""}} {{if ne .Icon ""}}
@ -8,7 +9,7 @@
<div class="card-header-icon"> <div class="card-header-icon">
<div class="tags has-addons"> <div class="tags has-addons">
<p class="tag">Status</p> <p class="tag">Status</p>
<p class="tag is-warning" id="{{.Title}}Status">Loading...</p> <p class="tag is-warning" id="StatusTag_{{$randID}}">Loading...</p>
</div> </div>
</div> </div>
</div> </div>
@ -26,13 +27,13 @@
request.onload = function () { request.onload = function () {
var data = JSON.parse(this.response) var data = JSON.parse(this.response)
if (data.down === true || parseInt(data.code) > 500 && parseInt(data.code) < 600 ) { if (data.down === true || parseInt(data.code) > 500 && parseInt(data.code) < 600 ) {
document.getElementById('{{.Title}}Status').classList.remove("is-warning") document.getElementById('StatusTag_{{$randID}}').classList.remove("is-warning")
document.getElementById('{{.Title}}Status').classList.add("is-danger") document.getElementById('StatusTag_{{$randID}}').classList.add("is-danger")
document.getElementById('{{.Title}}Status').innerHTML = "Offline" document.getElementById('StatusTag_{{$randID}}').innerHTML = "Offline"
} else { } else {
document.getElementById('{{.Title}}Status').classList.remove("is-warning") document.getElementById('StatusTag_{{$randID}}').classList.remove("is-warning")
document.getElementById('{{.Title}}Status').classList.add("is-success") document.getElementById('StatusTag_{{$randID}}').classList.add("is-success")
document.getElementById('{{.Title}}Status').innerHTML = "Online" document.getElementById('StatusTag_{{$randID}}').innerHTML = "Online"
} }
} }
request.send() request.send()

View File

@ -1,43 +1,44 @@
{{$randID := randAlphaNum 10}}
<div class="card-header"> <div class="card-header">
<p class="card-header-title">{{.Title}}</p> <p class="card-header-title">{{.Title}}</p>
</div> </div>
<div class="card-content"> <div class="card-content">
<p id="weatherLoadingText">Loading...</p> <p id="weatherLoadingText_{{$randID}}">Loading...</p>
<div class="columns is-mobile"> <div class="columns is-mobile">
<div class="column is-half"> <div class="column is-half">
<object type="image/svg+xml" id="weatherStateImg" style="width:45px; height: 45px"></object> <object type="image/svg+xml" id="weatherStateImg_{{$randID}}" style="width:45px; height: 45px"></object>
</div> </div>
<div class="column is-half"> <div class="column is-half">
<p id="weatherTempText" class="has-text-right subtitle"></p> <p id="weatherTempText_{{$randID}}" class="has-text-right subtitle"></p>
</div> </div>
</div> </div>
<p class="subtitle is-marginless" id="weatherStateText"></p> <p class="subtitle is-marginless" id="weatherStateText_{{$randID}}"></p>
<p id="weatherMinText"></p> <p id="weatherMinText_{{$randID}}"></p>
<p id="weatherMaxText"></p> <p id="weatherMaxText_{{$randID}}"></p>
<p id="weatherWindSpeedText"></p> <p id="weatherWindSpeedText_{{$randID}}"></p>
<p id="weatherHumidityText"></p> <p id="weatherHumidityText_{{$randID}}"></p>
<p id="weatherVisibilityText"></p> <p id="weatherVisibilityText_{{$randID}}"></p>
<p id="weatherPredictabilityText"></p> <p id="weatherPredictabilityText_{{$randID}}"></p>
</div> </div>
<div class="card-footer" style="margin-top: auto"> <div class="card-footer" style="margin-top: auto">
<span class="card-footer-item">Data from&nbsp;<a href="https://www.metaweather.com" class="has-text-info">Metaweather</a></span> <span class="card-footer-item">Data from&nbsp;<a href="https://www.metaweather.com" class="has-text-info">Metaweather</a></span>
</div> </div>
<script> <script>
var request = new XMLHttpRequest() var request = new XMLHttpRequest()
request.open('GET', "{{proxy (printf `https://www.metaweather.com/api/location/%s/` .Data.woeid) }}", true) request.open('GET', "{{proxy (printf `https://www.metaweather.com/api/location/%s/` .Data.woeid)}}", true)
const round = function (flt){return Number.parseFloat(flt).toPrecision(3)} const round = function (flt){return Number.parseFloat(flt).toPrecision(3)}
request.onload = function () { request.onload = function () {
const data = JSON.parse(this.response) const data = JSON.parse(this.response)
document.getElementById('weatherLoadingText').classList.add("is-hidden") document.getElementById('weatherLoadingText_{{$randID}}').classList.add("is-hidden")
document.getElementById('weatherStateText').innerText = data["consolidated_weather"][0]["weather_state_name"] document.getElementById('weatherStateText_{{$randID}}').innerText = data["consolidated_weather"][0]["weather_state_name"]
document.getElementById('weatherTempText').innerHTML = round(data["consolidated_weather"][0]["the_temp"]*1.8+32) + " &deg;F" document.getElementById('weatherTempText_{{$randID}}').innerHTML = round(data["consolidated_weather"][0]["the_temp"]*1.8+32) + " &deg;F"
document.getElementById('weatherStateImg').data = "/proxy/" + btoa("https://www.metaweather.com/static/img/weather/" + data["consolidated_weather"][0]["weather_state_abbr"] + ".svg") document.getElementById('weatherStateImg_{{$randID}}').data = "/proxy/" + btoa("https://www.metaweather.com/static/img/weather/" + data["consolidated_weather"][0]["weather_state_abbr"] + ".svg")
document.getElementById('weatherMinText').innerHTML = "Min: " + round(data["consolidated_weather"][0]["min_temp"]*1.8+32) + " &deg;F" document.getElementById('weatherMinText_{{$randID}}').innerHTML = "Min: " + round(data["consolidated_weather"][0]["min_temp"]*1.8+32) + " &deg;F"
document.getElementById('weatherMaxText').innerHTML = "Max: " + round(data["consolidated_weather"][0]["max_temp"]*1.8+32) + " &deg;F" document.getElementById('weatherMaxText_{{$randID}}').innerHTML = "Max: " + round(data["consolidated_weather"][0]["max_temp"]*1.8+32) + " &deg;F"
document.getElementById('weatherWindSpeedText').innerText = "Wind Speed: " + round(data["consolidated_weather"][0]["wind_speed"]) + "mph" document.getElementById('weatherWindSpeedText_{{$randID}}').innerText = "Wind Speed: " + round(data["consolidated_weather"][0]["wind_speed"]) + "mph"
document.getElementById('weatherHumidityText').innerText = "Humidity: " + data["consolidated_weather"][0]["humidity"] + "%" document.getElementById('weatherHumidityText_{{$randID}}').innerText = "Humidity: " + data["consolidated_weather"][0]["humidity"] + "%"
document.getElementById('weatherVisibilityText').innerText = "Visibility: " + round(data["consolidated_weather"][0]["visibility"]) + "mi" document.getElementById('weatherVisibilityText_{{$randID}}').innerText = "Visibility: " + round(data["consolidated_weather"][0]["visibility"]) + "mi"
document.getElementById('weatherPredictabilityText').innerText = "Predictability: " + data["consolidated_weather"][0]["predictability"] + "%" document.getElementById('weatherPredictabilityText_{{$randID}}').innerText = "Predictability: " + data["consolidated_weather"][0]["predictability"] + "%"
} }
request.send() request.send()
</script> </script>

View File

@ -5,6 +5,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"html/template" "html/template"
"regexp"
) )
// Function to dynamically execute template and return results // Function to dynamically execute template and return results
@ -33,6 +34,14 @@ func unescapeJS(s string) template.JS {
return template.JS(s) return template.JS(s)
} }
// Remove all non-alphanumeric characters
func toAlphaNum(s string) string {
// Create regex matching everything but alphanumeric
regex := regexp.MustCompile(`[^a-zA-Z0-9]+`)
// Remove all matched characters in string, then return
return regex.ReplaceAllString(s, "")
}
// Function to get template function map // Function to get template function map
func getFuncMap() template.FuncMap { func getFuncMap() template.FuncMap {
// Return function map with template functions // Return function map with template functions
@ -40,5 +49,6 @@ func getFuncMap() template.FuncMap {
"dyn_template": dynamicTemplate, "dyn_template": dynamicTemplate,
"proxy": wrapProxy, "proxy": wrapProxy,
"unescJS": unescapeJS, "unescJS": unescapeJS,
"toAlphaNum": toAlphaNum,
} }
} }