Hook up the web to the db
This commit is contained in:
parent
d71334cda7
commit
f89d5f5d05
2
Makefile
2
Makefile
@ -4,4 +4,4 @@ help: ## display this help
|
|||||||
@awk 'BEGIN{FS = ":.*##"; printf "\033[1m\nUsage\n \033[1;92m make\033[0;36m <target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } ' $(MAKEFILE_LIST)
|
@awk 'BEGIN{FS = ":.*##"; printf "\033[1m\nUsage\n \033[1;92m make\033[0;36m <target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } ' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
serve: ## Run "intake serve" with live reload
|
serve: ## Run "intake serve" with live reload
|
||||||
@air -build.cmd "go build -o scratch/intake" -build.bin scratch/intake -build.args_bin serve -build.exclude_dir scratch
|
@air -build.cmd "go build -o tmp/intake" -build.bin tmp/intake -build.args_bin serve,--db,tmp/db.sqlite
|
||||||
|
13
cmd/serve.go
13
cmd/serve.go
@ -11,10 +11,21 @@ var serveCmd = &cobra.Command{
|
|||||||
Long: `Serve the intake web interface.
|
Long: `Serve the intake web interface.
|
||||||
`,
|
`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
web.RunServer()
|
serve()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serveAddr string
|
||||||
|
var servePort string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(serveCmd)
|
rootCmd.AddCommand(serveCmd)
|
||||||
|
|
||||||
|
serveCmd.Flags().StringVarP(&serveAddr, "addr", "a", "localhost", "Address to bind to")
|
||||||
|
serveCmd.Flags().StringVarP(&servePort, "port", "p", "8081", "Port to bind to")
|
||||||
|
}
|
||||||
|
|
||||||
|
func serve() {
|
||||||
|
db := openAndMigrateDb()
|
||||||
|
web.RunServer(db, serveAddr, servePort)
|
||||||
}
|
}
|
||||||
|
@ -1 +1,20 @@
|
|||||||
{{ define "content" }}<h1>Hello {{ . }}</h1>{{ end }}
|
{{ define "title" }}Intake{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" -}}
|
||||||
|
<article>
|
||||||
|
<details>
|
||||||
|
<summary><span class="item-title">Sources</span></summary>
|
||||||
|
{{ if .Sources }}
|
||||||
|
<table class="intake-sources">
|
||||||
|
{{ range .Sources }}
|
||||||
|
<tr>
|
||||||
|
<td><a href="#">{{ .Name }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
||||||
|
{{ else }}
|
||||||
|
<p>No sources found.</p>
|
||||||
|
{{ end }}
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
{{- end }}
|
||||||
|
@ -6,6 +6,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed intake.css
|
||||||
|
var Stylesheet []byte
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
var templates embed.FS
|
var templates embed.FS
|
||||||
|
|
||||||
|
75
web/html/intake.css
Normal file
75
web/html/intake.css
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
article {
|
||||||
|
border: 1px solid black; border-radius: 6px;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.item-title {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
.item-button {
|
||||||
|
font-size: 1em;
|
||||||
|
float:right;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
.item-link {
|
||||||
|
text-decoration: none;
|
||||||
|
float:right;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 2px 7px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.item-info {
|
||||||
|
color: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
article img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
button, summary {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
summary {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
summary:focus {
|
||||||
|
outline: 1px dotted gray;
|
||||||
|
}
|
||||||
|
.strikethru span, .strikethru p {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.wide {
|
||||||
|
width: 100%;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.fade span, .fade p {
|
||||||
|
color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
table.feed-control td {
|
||||||
|
font-family: monospace; padding: 5px 10px;
|
||||||
|
}
|
||||||
|
.intake-sources td {
|
||||||
|
padding-block: 0.4em;
|
||||||
|
}
|
||||||
|
.intake-sources form {
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
article.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
article textarea {
|
||||||
|
width: 100%;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
span.error-message {
|
||||||
|
color: red;
|
||||||
|
}
|
@ -1,8 +1,13 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Intake</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ block "title" . }}Intake{{ end }}</title>
|
||||||
|
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMS41ZEdYUgAAAGFJREFUOE+lkFEKwDAIxXrzXXB3ckMm9EnAV/YRCxFCcUXEL3Jc77NDjpDA/VGL3RFWYEICfeGC8oQc9IPuCAnQDcoRVmBCAn3hgvKEHPSD7ggJ0A3KEVZgQgJ94YLSJ9YDUzNGDXGZ/JEAAAAASUVORK5CYII=">
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<main>
|
||||||
{{ template "content" . }}
|
{{ template "content" . }}
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
34
web/main.go
Normal file
34
web/main.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Jaculabilis/intake/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
db *core.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func logged(handler http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(writer http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Printf("%s %s", req.Method, req.URL.Path)
|
||||||
|
handler(writer, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFunc(pattern string, handler http.HandlerFunc) {
|
||||||
|
http.HandleFunc(pattern, logged(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunServer(db *core.DB, addr string, port string) {
|
||||||
|
env := &Env{db}
|
||||||
|
bind := net.JoinHostPort(addr, port)
|
||||||
|
|
||||||
|
handleFunc("/", env.rootHandler)
|
||||||
|
handleFunc("/style.css", env.styleHandler)
|
||||||
|
|
||||||
|
log.Fatal(http.ListenAndServe(bind, nil))
|
||||||
|
}
|
32
web/root.go
32
web/root.go
@ -1,19 +1,35 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Jaculabilis/intake/core"
|
||||||
"github.com/Jaculabilis/intake/web/html"
|
"github.com/Jaculabilis/intake/web/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
func rootHandler(writer http.ResponseWriter, req *http.Request) {
|
type SourceData struct {
|
||||||
log.Printf("%s %s", req.Method, req.URL.Path)
|
Name string
|
||||||
html.Home(writer, "world")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunServer() {
|
type HomeData struct {
|
||||||
http.HandleFunc("/", rootHandler)
|
Sources []SourceData
|
||||||
|
}
|
||||||
log.Fatal(http.ListenAndServe("localhost:8081", nil))
|
|
||||||
|
func (env *Env) rootHandler(writer http.ResponseWriter, req *http.Request) {
|
||||||
|
names, err := core.GetSources(env.db)
|
||||||
|
if err != nil {
|
||||||
|
writer.Write([]byte(err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sources []SourceData
|
||||||
|
for _, name := range names {
|
||||||
|
sources = append(sources, SourceData{name})
|
||||||
|
}
|
||||||
|
data := HomeData{sources}
|
||||||
|
html.Home(writer, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Env) styleHandler(writer http.ResponseWriter, req *http.Request) {
|
||||||
|
writer.Header()["Cache-Control"] = []string{"public, max-age=86400"}
|
||||||
|
writer.Write(html.Stylesheet)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user