Rework configuration to it's own package and make it available to web server. Start a template driven web interface.
This commit is contained in:
parent
55bb5a8bae
commit
46a0f5a187
@ -3,11 +3,11 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
open my $fh, "<", "dau.go" || die $!;
|
||||
open my $fh, "<", "config/config.go" || die $!;
|
||||
|
||||
my $version;
|
||||
while (<$fh>) {
|
||||
$version = $1 if /^const\s+currentVersion.*?"([\d\.]+)"/;
|
||||
$version = $1 if /^const\s+CurrentVersion.*?"([\d\.]+)"/;
|
||||
}
|
||||
close $fh;
|
||||
|
||||
|
13
config/config.go
Normal file
13
config/config.go
Normal file
@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
// Config for the application
|
||||
var Config struct {
|
||||
WebHookURL string
|
||||
Path string
|
||||
Watch int
|
||||
Username string
|
||||
NoWatermark bool
|
||||
Exclude string
|
||||
}
|
||||
|
||||
const CurrentVersion string = "0.6"
|
8
data/config.html
Normal file
8
data/config.html
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
<main role="main" class="inner DAU">
|
||||
<h1 class="DAU-heading">Config</h1>
|
||||
<p class="lead">Hey look it's DAU.</p>
|
||||
<p class="lead">
|
||||
<a href="#" class="btn btn-lg btn-secondary">Learn more</a>
|
||||
</p>
|
||||
</main>
|
106
data/dau.css
Normal file
106
data/dau.css
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Globals
|
||||
*/
|
||||
|
||||
/* Links */
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Custom default button */
|
||||
.btn-secondary,
|
||||
.btn-secondary:hover,
|
||||
.btn-secondary:focus {
|
||||
color: #333;
|
||||
text-shadow: none; /* Prevent inheritance from `body` */
|
||||
background-color: #fff;
|
||||
border: .05rem solid #fff;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Base structure
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
body {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
color: #fff;
|
||||
text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5);
|
||||
box-shadow: inset 0 0 5rem rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.cover-container {
|
||||
max-width: 42em;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Header
|
||||
*/
|
||||
.masthead {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.masthead-brand {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-masthead .nav-link {
|
||||
padding: .25rem 0;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, .5);
|
||||
background-color: transparent;
|
||||
border-bottom: .25rem solid transparent;
|
||||
}
|
||||
|
||||
.nav-masthead .nav-link:hover,
|
||||
.nav-masthead .nav-link:focus {
|
||||
border-bottom-color: rgba(255, 255, 255, .25);
|
||||
}
|
||||
|
||||
.nav-masthead .nav-link + .nav-link {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.nav-masthead .active {
|
||||
color: #fff;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.masthead-brand {
|
||||
float: left;
|
||||
}
|
||||
.nav-masthead {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Cover
|
||||
*/
|
||||
.cover {
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
.cover .btn-lg {
|
||||
padding: .75rem 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Footer
|
||||
*/
|
||||
.mastfoot {
|
||||
color: rgba(255, 255, 255, .5);
|
||||
}
|
@ -1 +1,8 @@
|
||||
THIS IS SPARTA
|
||||
|
||||
<main role="main" class="inner DAU">
|
||||
<h1 class="DAU-heading">Discord Auto Upload</h1>
|
||||
<p class="lead">Hey look it's DAU.</p>
|
||||
<p class="lead">
|
||||
<a href="#" class="btn btn-lg btn-secondary">Learn more</a>
|
||||
</p>
|
||||
</main>
|
||||
|
58
data/wrapper.tmpl
Normal file
58
data/wrapper.tmpl
Normal file
@ -0,0 +1,58 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>
|
||||
|
||||
|
||||
<style>
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
text-anchor: middle;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bd-placeholder-img-lg {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="/dau.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body class="text-center">
|
||||
|
||||
<div class="DAU-container d-flex w-100 h-100 p-3 mx-auto flex-column">
|
||||
<header class="masthead mb-auto">
|
||||
<div class="inner">
|
||||
<h3 class="masthead-brand">discord-auto-upload ({{.Version}})</h3>
|
||||
<nav class="nav nav-masthead justify-content-center">
|
||||
<a class="nav-link {{ if eq .Path "index.html"}} active {{ end }}" href="/">Home</a>
|
||||
<a class="nav-link {{ if eq .Path "config.html"}} active {{ end }}" href="/config.html">Config</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{.Body}}
|
||||
|
||||
<footer class="mastfoot mt-auto">
|
||||
<div class="inner">
|
||||
<!-- <p>DAU template for <a href="https://getbootstrap.com/">Bootstrap</a>, by <a href="https://twitter.com/mdo">@mdo</a>.</p> -->
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
86
dau.go
86
dau.go
@ -24,58 +24,39 @@ import (
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/pborman/getopt"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
// "github.com/skratchdot/open-golang/open"
|
||||
"golang.org/x/image/font/inconsolata"
|
||||
|
||||
"github.com/tardisx/discord-auto-upload/web"
|
||||
"github.com/tardisx/discord-auto-upload/config"
|
||||
)
|
||||
|
||||
const currentVersion = "0.7"
|
||||
|
||||
var lastCheck = time.Now()
|
||||
var newLastCheck = time.Now()
|
||||
|
||||
// Config for the application
|
||||
type Config struct {
|
||||
webhookURL string
|
||||
path string
|
||||
watch int
|
||||
username string
|
||||
noWatermark bool
|
||||
exclude string
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
config := parseOptions()
|
||||
checkPath(config.path)
|
||||
wconfig := web.Init()
|
||||
go processWebChanges(wconfig)
|
||||
parseOptions()
|
||||
checkPath(config.Config.Path)
|
||||
|
||||
log.Print("Opening web browser")
|
||||
open.Start("http://localhost:9090")
|
||||
// log.Print("Opening web browser")
|
||||
// open.Start("http://localhost:9090")
|
||||
go web.StartWebServer()
|
||||
|
||||
checkUpdates()
|
||||
|
||||
log.Print("Waiting for images to appear in ", config.path)
|
||||
log.Print("Waiting for images to appear in ", config.Config.Path)
|
||||
// wander the path, forever
|
||||
for {
|
||||
err := filepath.Walk(config.path,
|
||||
func(path string, f os.FileInfo, err error) error { return checkFile(path, f, err, config) })
|
||||
err := filepath.Walk(config.Config.Path,
|
||||
func(path string, f os.FileInfo, err error) error { return checkFile(path, f, err) })
|
||||
if err != nil {
|
||||
log.Fatal("could not watch path", err)
|
||||
}
|
||||
lastCheck = newLastCheck
|
||||
log.Print("sleeping before next check");
|
||||
time.Sleep(time.Duration(config.watch) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func processWebChanges(wc web.DAUWebServer) {
|
||||
for {
|
||||
change := <-wc.ConfigChange
|
||||
log.Print(change)
|
||||
log.Print("Got a change!")
|
||||
time.Sleep(time.Duration(config.Config.Watch) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,8 +99,8 @@ func checkUpdates() {
|
||||
log.Fatal("could not parse JSON: ", err)
|
||||
}
|
||||
|
||||
if currentVersion < latest.TagName {
|
||||
fmt.Printf("You are currently on version %s, but version %s is available\n", currentVersion, latest.TagName)
|
||||
if config.CurrentVersion < latest.TagName {
|
||||
fmt.Printf("You are currently on version %s, but version %s is available\n", config.CurrentVersion, latest.TagName)
|
||||
fmt.Println("----------- Release Info -----------")
|
||||
fmt.Println(latest.Body)
|
||||
fmt.Println("------------------------------------")
|
||||
@ -128,9 +109,8 @@ func checkUpdates() {
|
||||
|
||||
}
|
||||
|
||||
func parseOptions() Config {
|
||||
func parseOptions() {
|
||||
|
||||
var newConfig Config
|
||||
// Declare the flags to be used
|
||||
webhookFlag := getopt.StringLong("webhook", 'w', "", "discord webhook URL")
|
||||
pathFlag := getopt.StringLong("directory", 'd', "", "directory to scan, optional, defaults to current directory")
|
||||
@ -151,7 +131,7 @@ func parseOptions() Config {
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Println("dau - https://github.com/tardisx/discord-auto-upload")
|
||||
fmt.Printf("Version: %s\n", currentVersion)
|
||||
fmt.Printf("Version: %s\n", config.CurrentVersion)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@ -164,23 +144,22 @@ func parseOptions() Config {
|
||||
log.Fatal("ERROR: You must specify a --webhook URL")
|
||||
}
|
||||
|
||||
newConfig.path = *pathFlag
|
||||
newConfig.webhookURL = *webhookFlag
|
||||
newConfig.watch = int(*watchFlag)
|
||||
newConfig.username = *usernameFlag
|
||||
newConfig.noWatermark = *noWatermarkFlag
|
||||
newConfig.exclude = *excludeFlag
|
||||
config.Config.Path = *pathFlag
|
||||
config.Config.WebHookURL = *webhookFlag
|
||||
config.Config.Watch = int(*watchFlag)
|
||||
config.Config.Username = *usernameFlag
|
||||
config.Config.NoWatermark = *noWatermarkFlag
|
||||
config.Config.Exclude = *excludeFlag
|
||||
|
||||
return newConfig
|
||||
}
|
||||
|
||||
func checkFile(path string, f os.FileInfo, err error, config Config) error {
|
||||
func checkFile(path string, f os.FileInfo, err error) error {
|
||||
|
||||
if f.ModTime().After(lastCheck) && f.Mode().IsRegular() {
|
||||
|
||||
if fileEligible(config, path) {
|
||||
if fileEligible(path) {
|
||||
// process file
|
||||
processFile(config, path)
|
||||
processFile(path)
|
||||
}
|
||||
|
||||
if newLastCheck.Before(f.ModTime()) {
|
||||
@ -191,9 +170,9 @@ func checkFile(path string, f os.FileInfo, err error, config Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileEligible(config Config, file string) bool {
|
||||
func fileEligible(file string) bool {
|
||||
|
||||
if config.exclude != "" && strings.Contains(file, config.exclude) {
|
||||
if config.Config.Exclude != "" && strings.Contains(file, config.Config.Exclude) {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -205,9 +184,9 @@ func fileEligible(config Config, file string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func processFile(config Config, file string) {
|
||||
func processFile(file string) {
|
||||
|
||||
if !config.noWatermark {
|
||||
if !config.Config.NoWatermark {
|
||||
log.Print("Copying to temp location and watermarking ", file)
|
||||
file = mungeFile(file)
|
||||
}
|
||||
@ -216,8 +195,9 @@ func processFile(config Config, file string) {
|
||||
|
||||
extraParams := map[string]string{}
|
||||
|
||||
if config.username != "" {
|
||||
extraParams["username"] = config.username
|
||||
if config.Config.Username != "" {
|
||||
log.Print("Overriding username with " + config.Config.Username)
|
||||
extraParams["username"] = config.Config.Username
|
||||
}
|
||||
|
||||
type DiscordAPIResponseAttachment struct {
|
||||
@ -237,7 +217,7 @@ func processFile(config Config, file string) {
|
||||
var retriesRemaining = 5
|
||||
for retriesRemaining > 0 {
|
||||
|
||||
request, err := newfileUploadRequest(config.webhookURL, extraParams, "file", file)
|
||||
request, err := newfileUploadRequest(config.Config.WebHookURL, extraParams, "file", file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -296,7 +276,7 @@ func processFile(config Config, file string) {
|
||||
}
|
||||
}
|
||||
|
||||
if !config.noWatermark {
|
||||
if !config.Config.NoWatermark {
|
||||
log.Print("Removing temporary file ", file)
|
||||
os.Remove(file)
|
||||
}
|
||||
|
132
web/server.go
132
web/server.go
@ -8,6 +8,12 @@ import (
|
||||
"github.com/tardisx/discord-auto-upload/assets"
|
||||
// "strings"
|
||||
"regexp"
|
||||
"github.com/tardisx/discord-auto-upload/config"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"mime"
|
||||
"text/template"
|
||||
|
||||
)
|
||||
|
||||
// DAUWebServer - stuff for the web server
|
||||
@ -15,66 +21,124 @@ type DAUWebServer struct {
|
||||
ConfigChange chan int
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Success int
|
||||
type valueStringResponse struct {
|
||||
Success bool `json: 'success'`
|
||||
Value string `json: 'value'`
|
||||
}
|
||||
|
||||
// I am too noob to work out how to pass context around
|
||||
var wsConfig DAUWebServer
|
||||
|
||||
// r.ParseForm() // parse arguments, you have to call this by yourself
|
||||
// fmt.Println(r.Form) // print form information in server side
|
||||
// fmt.Println("path", r.URL.Path)
|
||||
// fmt.Println("scheme", r.URL.Scheme)
|
||||
// fmt.Println(r.Form["url_long"])
|
||||
// for k, v := range r.Form {
|
||||
// fmt.Println("key:", k)
|
||||
// fmt.Println("val:", strings.Join(v, ""))
|
||||
// }
|
||||
type errorResponse struct {
|
||||
Success bool `json: 'success'`
|
||||
Error string `json: 'error'`
|
||||
}
|
||||
|
||||
func getStatic(w http.ResponseWriter, r *http.Request) {
|
||||
// haha this is dumb and I should change it
|
||||
fmt.Println(r.URL)
|
||||
// fmt.Println(r.URL)
|
||||
re := regexp.MustCompile(`[^a-zA-Z0-9\.]`)
|
||||
path := r.URL.Path
|
||||
path := r.URL.Path[1:]
|
||||
sanitized_path := re.ReplaceAll([]byte(path), []byte("_"))
|
||||
|
||||
fmt.Println(sanitized_path)
|
||||
if string(sanitized_path) == "" {
|
||||
sanitized_path = []byte("index.html");
|
||||
}
|
||||
|
||||
data, err := assets.Asset(string(sanitized_path))
|
||||
if err != nil {
|
||||
// Asset was not found.
|
||||
fmt.Fprintln(w, err)
|
||||
}
|
||||
|
||||
|
||||
extension := filepath.Ext(string(sanitized_path))
|
||||
|
||||
// is this a HTML file? if so wrap it in the template
|
||||
if extension == ".html" {
|
||||
wrapper, _ := assets.Asset("wrapper.tmpl")
|
||||
t := template.Must(template.New("wrapper").Parse(string(wrapper)))
|
||||
var b struct {
|
||||
Body string
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
b.Body = string(data)
|
||||
b.Path = string(sanitized_path)
|
||||
b.Version = config.CurrentVersion
|
||||
t.Execute(w, b)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise we are a static thing
|
||||
w.Header().Set("Content-Type", mime.TypeByExtension(extension))
|
||||
|
||||
w.Write(data)
|
||||
//
|
||||
}
|
||||
|
||||
// TODO there should be locks around all these config accesses
|
||||
func getSetWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
wsConfig.ConfigChange <- 1
|
||||
goodResponse := response{1}
|
||||
js, err := json.Marshal(goodResponse)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(js)
|
||||
|
||||
if r.Method == "GET" {
|
||||
getResponse := valueStringResponse{ Success: true, Value: config.Config.WebHookURL }
|
||||
|
||||
// I can't see any way this will fail
|
||||
js, _ := json.Marshal(getResponse)
|
||||
w.Write(js)
|
||||
} else if r.Method == "POST" {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
config.Config.WebHookURL = r.PostForm.Get("value")
|
||||
postResponse := valueStringResponse{ Success: true, Value: config.Config.WebHookURL }
|
||||
|
||||
js, _ := json.Marshal(postResponse)
|
||||
w.Write(js)
|
||||
}
|
||||
}
|
||||
|
||||
func getSetDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
log.Print("ok")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if r.Method == "GET" {
|
||||
getResponse := valueStringResponse{ Success: true, Value: config.Config.Path }
|
||||
|
||||
// I can't see any way this will fail
|
||||
js, _ := json.Marshal(getResponse)
|
||||
w.Write(js)
|
||||
} else if r.Method == "POST" {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
newPath := r.PostForm.Get("value")
|
||||
|
||||
// sanity check this path
|
||||
stat, err := os.Stat(newPath)
|
||||
if os.IsNotExist(err) {
|
||||
// not exist
|
||||
response := errorResponse{ Success: false, Error: fmt.Sprintf("Path: %s - does not exist", newPath) }
|
||||
js, _ := json.Marshal(response)
|
||||
w.Write(js)
|
||||
return;
|
||||
} else if ! stat.IsDir() {
|
||||
// not a directory
|
||||
response := errorResponse{ Success: false, Error: fmt.Sprintf("Path: %s - is not a directory", newPath) }
|
||||
js, _ := json.Marshal(response)
|
||||
w.Write(js)
|
||||
return;
|
||||
}
|
||||
|
||||
config.Config.Path = newPath
|
||||
postResponse := valueStringResponse{ Success: true, Value: config.Config.Path }
|
||||
|
||||
js, _ := json.Marshal(postResponse)
|
||||
w.Write(js)
|
||||
}
|
||||
}
|
||||
|
||||
// Init is great
|
||||
func Init() DAUWebServer {
|
||||
wsConfig.ConfigChange = make(chan int)
|
||||
go startWebServer()
|
||||
return wsConfig
|
||||
}
|
||||
|
||||
func startWebServer() {
|
||||
func StartWebServer() {
|
||||
http.HandleFunc("/", getStatic)
|
||||
http.HandleFunc("/rest/config/webhook", getSetWebhook)
|
||||
http.HandleFunc("/rest/config/directory", getSetDirectory)
|
||||
|
Loading…
x
Reference in New Issue
Block a user