Static dir configuration and tree view of notes
This commit is contained in:
parent
99a15aa567
commit
cb9d95bcb2
@ -23,6 +23,10 @@ type Config struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Application struct {
|
||||||
|
StaticDir string
|
||||||
|
}
|
||||||
|
|
||||||
Session struct {
|
Session struct {
|
||||||
DaysValid int
|
DaysValid int
|
||||||
}
|
}
|
||||||
|
31
main.go
31
main.go
@ -59,11 +59,12 @@ func main() {// {{{
|
|||||||
connectionManager = NewConnectionManager()
|
connectionManager = NewConnectionManager()
|
||||||
go connectionManager.BroadcastLoop()
|
go connectionManager.BroadcastLoop()
|
||||||
|
|
||||||
static = http.FileServer(http.Dir("./static"))
|
static = http.FileServer(http.Dir(config.Application.StaticDir))
|
||||||
http.HandleFunc("/css_updated", cssUpdateHandler)
|
http.HandleFunc("/css_updated", cssUpdateHandler)
|
||||||
http.HandleFunc("/session/create", sessionCreate)
|
http.HandleFunc("/session/create", sessionCreate)
|
||||||
http.HandleFunc("/session/retrieve", sessionRetrieve)
|
http.HandleFunc("/session/retrieve", sessionRetrieve)
|
||||||
http.HandleFunc("/session/authenticate", sessionAuthenticate)
|
http.HandleFunc("/session/authenticate", sessionAuthenticate)
|
||||||
|
http.HandleFunc("/node/tree", nodeTree)
|
||||||
http.HandleFunc("/node/retrieve", nodeRetrieve)
|
http.HandleFunc("/node/retrieve", nodeRetrieve)
|
||||||
http.HandleFunc("/node/create", nodeCreate)
|
http.HandleFunc("/node/create", nodeCreate)
|
||||||
http.HandleFunc("/node/update", nodeUpdate)
|
http.HandleFunc("/node/update", nodeUpdate)
|
||||||
@ -185,6 +186,32 @@ func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{
|
|||||||
})
|
})
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
|
func nodeTree(w http.ResponseWriter, r *http.Request) {// {{{
|
||||||
|
var err error
|
||||||
|
var session Session
|
||||||
|
|
||||||
|
if session, _, err = ValidateSession(r, true); err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct { StartNodeID int }{}
|
||||||
|
if err = parseRequest(r, &req); err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, err := session.NodeTree(req.StartNodeID)
|
||||||
|
if err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData(w, map[string]interface{}{
|
||||||
|
"OK": true,
|
||||||
|
"Nodes": nodes,
|
||||||
|
})
|
||||||
|
}// }}}
|
||||||
func nodeRetrieve(w http.ResponseWriter, r *http.Request) {// {{{
|
func nodeRetrieve(w http.ResponseWriter, r *http.Request) {// {{{
|
||||||
log.Println("/node/retrieve")
|
log.Println("/node/retrieve")
|
||||||
var err error
|
var err error
|
||||||
@ -332,7 +359,7 @@ func newTemplate(requestPath string) (tmpl *template.Template, err error) {// {{
|
|||||||
if p[len(p)-1] == '/' {
|
if p[len(p)-1] == '/' {
|
||||||
p += "index.html"
|
p += "index.html"
|
||||||
}
|
}
|
||||||
p = "static" + p
|
p = config.Application.StaticDir + p
|
||||||
|
|
||||||
base := path.Base(p)
|
base := path.Base(p)
|
||||||
if tmpl, err = template.New(base).ParseFiles(p); err != nil { return }
|
if tmpl, err = template.New(base).ParseFiles(p); err != nil { return }
|
||||||
|
66
node.go
66
node.go
@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@ -14,11 +14,75 @@ type Node struct {
|
|||||||
ParentID int `db:"parent_id"`
|
ParentID int `db:"parent_id"`
|
||||||
Name string
|
Name string
|
||||||
Content string
|
Content string
|
||||||
|
Updated time.Time
|
||||||
Children []Node
|
Children []Node
|
||||||
Crumbs []Node
|
Crumbs []Node
|
||||||
Complete bool
|
Complete bool
|
||||||
|
Level int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session Session) NodeTree(startNodeID int) (nodes []Node, err error) {// {{{
|
||||||
|
var rows *sqlx.Rows
|
||||||
|
rows, err = db.Queryx(`
|
||||||
|
WITH RECURSIVE nodetree AS (
|
||||||
|
SELECT
|
||||||
|
*,
|
||||||
|
array[name::text] AS path,
|
||||||
|
0 AS level
|
||||||
|
FROM node
|
||||||
|
WHERE
|
||||||
|
user_id = $1 AND
|
||||||
|
CASE $2::int
|
||||||
|
WHEN 0 THEN parent_id IS NULL
|
||||||
|
ELSE parent_id = $2
|
||||||
|
END
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
n.*,
|
||||||
|
path||n.name::text AS path,
|
||||||
|
nt.level + 1 AS level
|
||||||
|
FROM node n
|
||||||
|
INNER JOIN nodetree nt ON n.parent_id = nt.id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
user_id,
|
||||||
|
COALESCE(parent_id, 0) AS parent_id,
|
||||||
|
name,
|
||||||
|
updated,
|
||||||
|
level
|
||||||
|
FROM nodetree
|
||||||
|
ORDER BY
|
||||||
|
path ASC
|
||||||
|
`,
|
||||||
|
session.UserID,
|
||||||
|
startNodeID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
type resultRow struct {
|
||||||
|
Node
|
||||||
|
Level int
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = []Node{}
|
||||||
|
for rows.Next() {
|
||||||
|
node := Node{}
|
||||||
|
node.Complete = false
|
||||||
|
if err = rows.StructScan(&node); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}// }}}
|
||||||
func (session Session) RootNode() (node Node, err error) {// {{{
|
func (session Session) RootNode() (node Node, err error) {// {{{
|
||||||
var rows *sqlx.Rows
|
var rows *sqlx.Rows
|
||||||
rows, err = db.Queryx(`
|
rows, err = db.Queryx(`
|
||||||
|
@ -158,6 +158,9 @@ header .menu {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
.tree {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
@media only screen and (max-width: 100ex) {
|
@media only screen and (max-width: 100ex) {
|
||||||
.node-content {
|
.node-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -7,6 +7,7 @@ export class NodeUI extends Component {
|
|||||||
constructor() {//{{{
|
constructor() {//{{{
|
||||||
super()
|
super()
|
||||||
this.menu = signal(false)
|
this.menu = signal(false)
|
||||||
|
this.tree = signal(null)
|
||||||
this.node = signal(null)
|
this.node = signal(null)
|
||||||
this.nodeContent = createRef()
|
this.nodeContent = createRef()
|
||||||
window.addEventListener('popstate', evt=>{
|
window.addEventListener('popstate', evt=>{
|
||||||
@ -21,6 +22,11 @@ export class NodeUI extends Component {
|
|||||||
return
|
return
|
||||||
|
|
||||||
let node = this.node.value
|
let node = this.node.value
|
||||||
|
let tree = this.tree.value
|
||||||
|
|
||||||
|
let treeHTML = html`Tree`
|
||||||
|
if(tree !== null)
|
||||||
|
treeHTML = this.renderTree(tree)
|
||||||
|
|
||||||
let crumbs = [
|
let crumbs = [
|
||||||
html`<div class="crumb" onclick=${()=>this.goToNode(0)}>Start</div>`
|
html`<div class="crumb" onclick=${()=>this.goToNode(0)}>Start</div>`
|
||||||
@ -58,6 +64,10 @@ export class NodeUI extends Component {
|
|||||||
<div class="crumbs">${crumbs}</crumbs>
|
<div class="crumbs">${crumbs}</crumbs>
|
||||||
|
|
||||||
${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``}
|
${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``}
|
||||||
|
<div class="tree" onclick=${()=>this.retrieveTree()} style="color: #000">
|
||||||
|
<div class="node">Start</div>
|
||||||
|
${treeHTML}
|
||||||
|
</div>
|
||||||
|
|
||||||
${node.ID > 0 ? html`
|
${node.ID > 0 ? html`
|
||||||
<div class="node-name">${node.Name}</div>
|
<div class="node-name">${node.Name}</div>
|
||||||
@ -146,6 +156,16 @@ export class NodeUI extends Component {
|
|||||||
})
|
})
|
||||||
.catch(this.props.app.responseError)
|
.catch(this.props.app.responseError)
|
||||||
}//}}}
|
}//}}}
|
||||||
|
retrieveTree() {//{{{
|
||||||
|
this.props.app.request('/node/tree', { StartNodeID: this.node.value.ID })
|
||||||
|
.then(res=>{
|
||||||
|
this.tree.value = res.Nodes
|
||||||
|
})
|
||||||
|
.catch(this.props.app.responseError)
|
||||||
|
}//}}}
|
||||||
|
renderTree(tree) {//{{{
|
||||||
|
return tree.map(node=>html`<div class="node" style="margin-left: ${(node.Level+1) * 32}px">${node.Name}</div>`)
|
||||||
|
}//}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeContent extends Component {
|
class NodeContent extends Component {
|
||||||
|
@ -188,6 +188,10 @@ header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 100ex) {
|
@media only screen and (max-width: 100ex) {
|
||||||
.node-content {
|
.node-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
Loading…
Reference in New Issue
Block a user