Initial commit

This commit is contained in:
Sasha Koshka
2023-05-25 18:08:56 -04:00
commit c300567c0c
51 changed files with 42251 additions and 0 deletions

95
examples/board/main.go Normal file
View File

@@ -0,0 +1,95 @@
package main
import "log"
import "sync"
import "path"
import "net/http"
import "crypto/tls"
import "html/template"
import "hnakra/service"
type Post struct {
Author, Content string
Next *Post
}
type Board struct {
root string
mount *service.HTTP
mux *http.ServeMux
template *template.Template
postsMutex sync.RWMutex
posts *Post
}
func main () {
board := Board { root: "/board/" }
board.mux = http.NewServeMux()
board.mount = &service.HTTP {
Mount: service.Mount {
Path: board.root,
Name: "Board",
Description: "A board where you can post things.",
TLSConfig: &tls.Config { InsecureSkipVerify: true },
},
Handler: board.mux,
}
handle := func (pattern string, handler func (http.ResponseWriter, *http.Request)) {
board.mux.HandleFunc(pattern, handler)
}
handle(board.root, board.serveHome)
handle(path.Join(board.root, "actionPost"), board.serveActionPost)
board.template = template.Must(template.New("board").Parse(tem))
err := board.mount.Run()
if err != nil {
log.Println("XXX", err)
}
}
func (board *Board) getPosts (max int) []*Post {
board.postsMutex.RLock()
defer board.postsMutex.RUnlock()
posts := make([]*Post, max)
count := 0
current := board.posts
for current != nil && count < max {
posts[count] = current
count ++
current = current.Next
}
return posts[:count]
}
func (board *Board) serveHome (res http.ResponseWriter, req *http.Request) {
if req.URL.Path != board.root {
http.NotFoundHandler().ServeHTTP(res, req)
return
}
res.Header().Add("content-type", "text/html")
res.WriteHeader(http.StatusOK)
board.template.ExecuteTemplate(res, "pageHome", board.getPosts(16))
}
func (board *Board) serveActionPost (res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
res.Header().Add("content-type", "text/plain")
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte("Method not allowed"))
return
}
defer http.Redirect(res, req, board.root, http.StatusSeeOther)
req.ParseForm()
author := req.PostFormValue("author")
content := req.PostFormValue("content")
board.posts = &Post {
Author: author,
Content: content,
Next: board.posts,
}
}

150
examples/board/template.go Normal file
View File

@@ -0,0 +1,150 @@
package main
const tem = `
{{define "top"}}
<!DOCTYPE html>
<html>
<head>
<title>{{.}}</title>
<style>
* {
appearance: none;
--webkit-appearance: none;
font-family: monospace;
box-sizing: border-box;
color: inherit;
font-size: 12pt;
}
body {
background-color: #b3b5b1;
color: #272825;
margin: 1em;
}
body > * {
margin: auto;
max-width: 40em;
padding: 1em;
}
header {
background-color: #a2a89d;
border: 1px solid #8f938c;
border-bottom-color: #7f847b;
border-radius: 4px 4px 0 0;
text-shadow: 1px 1px 1px #b8bfb0;
color: #4c4f49;
}
main {
background-color: #abaea8;
border: 1px solid #959b91;
border-top-color: #a1a59e;
border-radius: 0 0 4px 4px;
}
h1 { font-size: 1.5em }
hr {
margin: 1em 0;
border: none;
border-top: 1px solid #7f847b9e;
border-bottom: 1px solid #56604f1c;
}
.formRow {
margin: 1em 0;
}
button, input, textarea {
margin: 0;
border: 1px solid #7f847b;
background-color: #b3b5b1;
padding: 0.25em 0.5em;
box-shadow: 1px 1px 0 inset #56604f38;
border-radius: 2px;
font-weight: normal;
}
textarea {
height: 4em;
}
button:not(:active), input[type=submit]:not(:active) {
box-shadow: -1px -1px 0 inset #56604f38;
}
button:active, input[type=submit]:active {
border-color: #757a71;
padding-top: calc(0.25em + 1px);
padding-right: calc(0.5em - 1px);
padding-bottom: calc(0.25em - 1px);
padding-left: calc(0.5em + 1px);
}
.post {
padding: 0.4em 0.5em;
background-color: #a2a69f;
border-radius: 2px;
border: 1px solid #8f938c;
margin: 1em 0;
}
.post .author {
font-size: 0.7em;
color: #5e625a;
text-shadow: 1px 1px 0 #b3b5b1;
margin-bottom: 0.3em;
}
*:last-child { margin-bottom: 0 }
*:first-child { margin-top: 0 }
</style>
</head>
<body>
<header>
<h1>{{.}}</h1>
</header>
<main>
{{end}}
{{define "bottom"}}</main></body></html>{{end}}
{{define "post"}}
<div class=post>
<div class=author>{{.Author}}</div>
<div class=content>{{.Content}}</div>
</div>
{{end}}
{{define "postForm"}}
<form method=POST action=actionPost>
<div class=formRow>
<input type=text name=author id=author placeholder="Name" required>
</div>
<div class=formRow>
<textarea name=content id=content placeholder="Post content" required></textarea>
</div>
<div class=formRow>
<input type=submit value="Post">
</div>
</form>
{{end}}
{{define "pageGeneric"}}
{{template "top" .Title}}
<p>{{.Content}}</p>
{{template "bottom"}}
{{end}}
{{define "pageHome"}}
{{template "top" "Home"}}
{{template "postForm"}}
<hr>
{{range .}}
{{template "post" .}}
{{end}}
{{template "bottom"}}
{{end}}
`