Also structure CLI functions to allow for `skouter dev seed` command.master
@@ -8,3 +8,6 @@ DBPass | |||
Uses ESbuild and the Vue node package. | |||
`sudo apt install esbuild; npm install vue;` | |||
`` | |||
### Starting Server | |||
It reads database info from environment variables, unless called as `skouter dev`. Dev mode uses default datbase credentials for user 'tester'. |
@@ -45,13 +45,6 @@ v-else-if="active == 4" /> | |||
<login @login="start" /> | |||
</template> | |||
<estimate-test | |||
v-else-if="active == 7" | |||
:token="token" | |||
:estimate="preview" | |||
:user="user" | |||
/> | |||
</div> | |||
</template> | |||
@@ -61,7 +54,6 @@ import Spinner from "./spinner.vue" | |||
import Home from "./home.vue" | |||
import NewEstimate from "./new/new.vue" | |||
import Estimates from "./estimates.vue" | |||
import EstimateTest from "./estimate-test.vue" | |||
import Settings from "./settings.vue" | |||
import SignOut from "./sign-out.vue" | |||
import Login from "./login.vue" | |||
@@ -1,137 +0,0 @@ | |||
<template> | |||
<div id="pdf-doc" ref="doc" v-if="estimate"> | |||
<div class="disclaimer"><p>Actual costs may vary from estimates after approval. Get an official quote before choosing a loan.</p></div> | |||
<header class="heading"> | |||
<img :src="letterhead" /> | |||
<div> | |||
<div class="user-info"> | |||
<h4>{{user.firstName + " " + user.lastName}}</h4> | |||
<span>{{user.email}}</span> | |||
<span>{{user.phone}}</span> | |||
<small>{{user.address.street}}</small> | |||
<small> | |||
{{`${user.address.city}, ${user.address.region} ${user.address.zip}`}} | |||
</small> | |||
</div> | |||
<img :src="avatar"/> | |||
</div> | |||
</header> | |||
<button @click="getPdf">Generate</button> | |||
<a :href="pdfLink" v-if="pdfLink" download="estimate.pdf">download </a> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { ref, computed, onMounted } from "vue" | |||
import html2pdf from "html2pdf.js"; | |||
const doc = ref(null) | |||
const props = defineProps(['token', 'estimate', 'user']) | |||
const estimate = ref(null) | |||
const estimates = ref(null) | |||
const pdfLink = ref('') | |||
const letterhead = computed(() => { | |||
if (!props.user.letterhead) return null | |||
return URL.createObjectURL(props.user.letterhead) | |||
}) | |||
const avatar = computed(() => { | |||
if (!props.user.letterhead) return null | |||
console.log(props.user) | |||
return URL.createObjectURL(props.user.avatar) | |||
}) | |||
function makePDF() { | |||
var opt = { | |||
image: { type: 'png', quality: 1 }, | |||
} | |||
html2pdf(doc.value, opt) | |||
} | |||
function getEstimates() { | |||
return fetch(`/api/estimates`, | |||
{method: 'GET', | |||
headers: { | |||
"Accept": "application/json", | |||
"Authorization": `Bearer ${props.token}`, | |||
}, | |||
}).then(response => { | |||
if (response.ok) { return response.json() } else { | |||
response.text().then(t => console.log(t)) | |||
} | |||
}).then (result => { | |||
if (!result || !result.length) return // Exit if token is invalid or no fees are saved | |||
estimates.value = result | |||
// console.log(result) | |||
}) | |||
} | |||
function getPdf() { | |||
fetch(`/api/pdf`, | |||
{method: 'POST', | |||
body: JSON.stringify(estimate.value), | |||
headers: { | |||
"Accept": "application/json", | |||
"Authorization": `Bearer ${props.token}`, | |||
}, | |||
}).then(response => { | |||
if (response.ok) { return response.blob() } | |||
else { | |||
return null | |||
} | |||
}).then (result => { | |||
if (!result) return | |||
pdfLink.value = URL.createObjectURL(result) | |||
}) | |||
} | |||
onMounted(() => { | |||
getEstimates().then(() => estimate.value = estimates.value[0]) | |||
}) | |||
</script> | |||
<style scoped> | |||
#pdf-doc { | |||
margin: 4px 30px; | |||
} | |||
.disclaimer { | |||
font-weight: bold; | |||
border-bottom: 1px solid lightgrey; | |||
margin-bottom: 20px; | |||
color: var(--text); | |||
} | |||
.disclaimer p { | |||
margin: 5px 0; | |||
} | |||
h4 { | |||
margin: 4px 0; | |||
} | |||
header.heading { | |||
display: flex; | |||
justify-content: space-between; | |||
} | |||
.user-info { | |||
display: flex; | |||
flex-flow: column; | |||
} | |||
#pdf-doc header.heading > div { | |||
display: flex; | |||
gap: 8px; | |||
text-align: right; | |||
} | |||
</style> |
@@ -4,6 +4,7 @@ go 1.19 | |||
require ( | |||
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0 | |||
github.com/brianvoe/gofakeit/v6 v6.23.2 | |||
github.com/disintegration/gift v1.2.1 | |||
github.com/go-sql-driver/mysql v1.6.0 | |||
github.com/golang-jwt/jwt/v4 v4.5.0 | |||
@@ -1,5 +1,7 @@ | |||
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0 h1:DNrExYwvyyI404SxdUCCANAj9TwnGjRfa3cYFMNY1AU= | |||
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0/go.mod h1:SQq4xfIdvf6WYKSDxAJc+xOJdolt+/bc1jnQKMtPMvQ= | |||
github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8= | |||
github.com/brianvoe/gofakeit/v6 v6.23.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= | |||
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= | |||
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= | |||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | |||
@@ -24,6 +24,7 @@ import ( | |||
// pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf" | |||
"github.com/golang-jwt/jwt/v4" | |||
"github.com/disintegration/gift" | |||
"github.com/brianvoe/gofakeit/v6" | |||
"image" | |||
"image/png" | |||
_ "image/jpeg" | |||
@@ -195,7 +196,7 @@ var paths = map[string]string { | |||
var pages = map[string]Page { | |||
"home": cache("home", "Home"), | |||
"terms": cache("terms", "Terms and Conditions"), | |||
"test": cachePdf("comparison"), | |||
"report": cachePdf("comparison"), | |||
"app": cache("app", "App"), | |||
} | |||
@@ -1846,10 +1847,7 @@ func validateEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||
if err != nil { http.Error(w, err.Error(), 406); return } | |||
} | |||
func showPDF(w http.ResponseWriter, r *http.Request) { | |||
// var args []string | |||
// p := r.URL.Path | |||
func checkPdf(w http.ResponseWriter, r *http.Request) { | |||
db, err := sql.Open("mysql", | |||
fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter_dev", | |||
os.Getenv("DBUser"), | |||
@@ -1863,8 +1861,6 @@ func showPDF(w http.ResponseWriter, r *http.Request) { | |||
// maybe os.Exit(1) instead | |||
} | |||
page := cachePdf("comparison") | |||
estimates, err := getEstimates(db, 1, 0) | |||
if err != nil { w.WriteHeader(500); return } | |||
@@ -1894,7 +1890,7 @@ func showPDF(w http.ResponseWriter, r *http.Request) { | |||
} | |||
} | |||
err = page.tpl.ExecuteTemplate(w, "master.tpl", info) | |||
err = pages["report"].tpl.ExecuteTemplate(w, "master.tpl", info) | |||
if err != nil {fmt.Println(err)} | |||
} | |||
@@ -1944,7 +1940,7 @@ func getPdf(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||
base64.StdEncoding.EncodeToString(letterhead) | |||
err = pages["test"].tpl.ExecuteTemplate(stdin, "master.tpl", info) | |||
err = pages["report"].tpl.ExecuteTemplate(stdin, "master.tpl", info) | |||
if err != nil { | |||
w.WriteHeader(500); | |||
log.Println(err) | |||
@@ -1961,7 +1957,6 @@ func getPdf(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||
} | |||
if err := cmd.Wait(); err != nil { | |||
// w.WriteHeader(500) | |||
log.Println(err) | |||
return | |||
} | |||
@@ -2117,7 +2112,7 @@ func route(w http.ResponseWriter, r *http.Request) { | |||
case match(p, "/app", &args): | |||
page = pages[ "app" ] | |||
case match(p, "/test", &args): | |||
showPDF(w, r) | |||
checkPdf(w, r) | |||
return | |||
default: | |||
http.NotFound(w, r) | |||
@@ -2136,21 +2131,62 @@ func serve() { | |||
log.Fatal(http.ListenAndServe(address, nil)) | |||
} | |||
func dbSeed() { | |||
db, err := sql.Open("mysql", | |||
fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s", | |||
os.Getenv("DBUser"), | |||
os.Getenv("DBPass"), | |||
os.Getenv("DBName"), | |||
)) | |||
err = db.Ping() | |||
if err != nil { | |||
fmt.Println("Bad database configuration: %v\n", err) | |||
panic(err) | |||
// maybe os.Exit(1) instead | |||
} | |||
} | |||
func dev(args []string) { | |||
if len(args) == 0 { | |||
os.Setenv("DBName", "skouter_dev") | |||
os.Setenv("DBUser", "tester") | |||
os.Setenv("DBPass", "test123") | |||
serve() | |||
return | |||
} | |||
switch args[0] { | |||
case "seed": | |||
dbSeed() | |||
default: | |||
return | |||
} | |||
} | |||
func check(args []string) { | |||
os.Setenv("DBName", "skouter_dev") | |||
os.Setenv("DBUser", "tester") | |||
os.Setenv("DBPass", "test123") | |||
files := http.FileServer(http.Dir("")) | |||
http.Handle("/assets/", files) | |||
http.HandleFunc("/", checkPdf) | |||
log.Fatal(http.ListenAndServe(address, nil)) | |||
} | |||
func main() { | |||
if len(os.Args) <= 1 { | |||
serve() | |||
return | |||
} | |||
if os.Args[1] == "dev" { | |||
switch os.Args[1] { | |||
case "dev": | |||
dev(os.Args[2:]) | |||
} | |||
case "check": | |||
check(os.Args[2:]) | |||
default: | |||
return | |||
} | |||
} |