Compare commits
4 Commits
48e07c15d4
...
6cb4833dbd
Author | SHA1 | Date | |
---|---|---|---|
|
6cb4833dbd | ||
|
aef8f1a572 | ||
|
392aff5956 | ||
|
ac58fa0fc8 |
36
README.md
Normal file
36
README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Run from scratch
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
Create an empty database. The configured user needs to be able to create and alter stuff.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Create a configuration file `$HOME/.config/notes.yaml` with the following content:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
websocket:
|
||||||
|
domains:
|
||||||
|
- notes.com
|
||||||
|
|
||||||
|
database:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 5432
|
||||||
|
name: notes.com
|
||||||
|
username: notes
|
||||||
|
password: SomePassword
|
||||||
|
|
||||||
|
application:
|
||||||
|
directories:
|
||||||
|
static: /usr/local/share/notes
|
||||||
|
upload: /usr/local/lib/notes/upload
|
||||||
|
|
||||||
|
session:
|
||||||
|
daysvalid: 31
|
||||||
|
```
|
||||||
|
|
||||||
|
## User
|
||||||
|
|
||||||
|
Create a user by running
|
||||||
|
```
|
||||||
|
notes --createuser
|
||||||
|
```
|
1
TODO
1
TODO
@ -4,7 +4,6 @@
|
|||||||
- per file
|
- per file
|
||||||
- when deleting node and child nodes
|
- when deleting node and child nodes
|
||||||
* Move node
|
* Move node
|
||||||
* Tree titles should be user-select none
|
|
||||||
|
|
||||||
|
|
||||||
Long term
|
Long term
|
||||||
|
4
db.go
4
db.go
@ -6,7 +6,6 @@ import (
|
|||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
"embed"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -16,9 +15,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
dbConn string
|
dbConn string
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
|
|
||||||
//go:embed sql/*
|
|
||||||
embedded embed.FS
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func dbInit() (err error) {// {{{
|
func dbInit() (err error) {// {{{
|
||||||
|
57
main.go
57
main.go
@ -3,8 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
// Standard
|
// Standard
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"embed"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"path/filepath"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -13,34 +13,40 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
_ "embed"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "v13";
|
const LISTEN_HOST = "0.0.0.0"
|
||||||
const LISTEN_HOST = "0.0.0.0";
|
|
||||||
const DB_SCHEMA = 13
|
const DB_SCHEMA = 13
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagPort int
|
flagPort int
|
||||||
flagVersion bool
|
flagVersion bool
|
||||||
|
flagCreateUser bool
|
||||||
|
flagConfig string
|
||||||
|
|
||||||
connectionManager ConnectionManager
|
connectionManager ConnectionManager
|
||||||
static http.Handler
|
static http.Handler
|
||||||
config Config
|
config Config
|
||||||
|
VERSION string
|
||||||
|
|
||||||
|
//go:embed version sql/*
|
||||||
|
embedded embed.FS
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { // {{{
|
func init() { // {{{
|
||||||
flag.IntVar(
|
version, _ := embedded.ReadFile("version")
|
||||||
&flagPort,
|
VERSION = strings.TrimSpace(string(version))
|
||||||
"port",
|
|
||||||
1371,
|
|
||||||
"TCP port to listen on",
|
|
||||||
)
|
|
||||||
flag.BoolVar(&flagVersion, "version", false, "Shows Notes version and exists")
|
|
||||||
|
|
||||||
|
configFilename := os.Getenv("HOME") + "/.config/notes.yaml"
|
||||||
|
flag.IntVar(&flagPort, "port", 1371, "TCP port to listen on")
|
||||||
|
flag.BoolVar(&flagVersion, "version", false, "Shows Notes version and exists")
|
||||||
|
flag.BoolVar(&flagCreateUser, "createuser", false, "Create a user and exit")
|
||||||
|
flag.StringVar(&flagConfig, "config", configFilename, "Filename of configuration file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
@ -57,7 +63,7 @@ func main() {// {{{
|
|||||||
|
|
||||||
log.Printf("\x1b[32mNotes\x1b[0m %s\n", VERSION)
|
log.Printf("\x1b[32mNotes\x1b[0m %s\n", VERSION)
|
||||||
|
|
||||||
config, err = ConfigRead(os.Getenv("HOME")+"/.config/notes.yaml")
|
config, err = ConfigRead(flagConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s\n", err)
|
fmt.Printf("%s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -68,6 +74,15 @@ func main() {// {{{
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flagCreateUser {
|
||||||
|
err = createUser()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
connectionManager = NewConnectionManager()
|
connectionManager = NewConnectionManager()
|
||||||
go connectionManager.BroadcastLoop()
|
go connectionManager.BroadcastLoop()
|
||||||
|
|
||||||
@ -100,7 +115,11 @@ func main() {// {{{
|
|||||||
|
|
||||||
func cssUpdateHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
func cssUpdateHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
log.Println("[BROADCAST] CSS updated")
|
log.Println("[BROADCAST] CSS updated")
|
||||||
connectionManager.Broadcast(struct{ Ok bool; ID string; Op string }{ Ok: true, Op: "css_reload" })
|
connectionManager.Broadcast(struct {
|
||||||
|
Ok bool
|
||||||
|
ID string
|
||||||
|
Op string
|
||||||
|
}{Ok: true, Op: "css_reload"})
|
||||||
} // }}}
|
} // }}}
|
||||||
func websocketHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
func websocketHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
var err error
|
var err error
|
||||||
@ -192,7 +211,10 @@ func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req := struct{ Username string; Password string }{}
|
req := struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}{}
|
||||||
if err = parseRequest(r, &req); err != nil {
|
if err = parseRequest(r, &req); err != nil {
|
||||||
responseError(w, err)
|
responseError(w, err)
|
||||||
return
|
return
|
||||||
@ -444,7 +466,6 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{
|
|||||||
md5sumBytes := md5.Sum(fileBytes)
|
md5sumBytes := md5.Sum(fileBytes)
|
||||||
md5sum := hex.EncodeToString(md5sumBytes[:])
|
md5sum := hex.EncodeToString(md5sumBytes[:])
|
||||||
|
|
||||||
|
|
||||||
var nodeID int
|
var nodeID int
|
||||||
if nodeID, err = strconv.Atoi(r.PostFormValue("NodeID")); err != nil {
|
if nodeID, err = strconv.Atoi(r.PostFormValue("NodeID")); err != nil {
|
||||||
responseError(w, err)
|
responseError(w, err)
|
||||||
@ -704,7 +725,9 @@ func newTemplate(requestPath string) (tmpl *template.Template, err error) {// {{
|
|||||||
p = config.Application.Directories.Static + p
|
p = config.Application.Directories.Static + 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
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
@ -205,6 +205,7 @@ header .menu {
|
|||||||
#tree .node .name {
|
#tree .node .name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
#tree .node .name:hover {
|
#tree .node .name:hover {
|
||||||
color: #ecbf00;
|
color: #ecbf00;
|
||||||
|
@ -232,6 +232,7 @@ header {
|
|||||||
.name {
|
.name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @accent_2;
|
color: @accent_2;
|
||||||
|
36
user.go
36
user.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
// Standard
|
// Standard
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (session Session) UpdatePassword(currPass, newPass string) (ok bool, err error) {
|
func (session Session) UpdatePassword(currPass, newPass string) (ok bool, err error) {
|
||||||
@ -35,3 +36,38 @@ func (session Session) UpdatePassword(currPass, newPass string) (ok bool, err er
|
|||||||
|
|
||||||
return rowsAffected > 0, nil
|
return rowsAffected > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createUser() (err error) {
|
||||||
|
var username, password string
|
||||||
|
fmt.Printf("Username: ")
|
||||||
|
fmt.Scanln(&username)
|
||||||
|
fmt.Printf("Password: ")
|
||||||
|
fmt.Scanln(&password)
|
||||||
|
|
||||||
|
err = CreateDbUser(username, password)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateDbUser(username string, password string) (err error) {
|
||||||
|
var result sql.Result
|
||||||
|
|
||||||
|
result, err = db.Exec(`
|
||||||
|
INSERT INTO public.user(username, password)
|
||||||
|
VALUES(
|
||||||
|
$1,
|
||||||
|
password_hash(
|
||||||
|
/* salt in hex */
|
||||||
|
ENCODE(gen_random_bytes(16), 'hex'),
|
||||||
|
|
||||||
|
/* password */
|
||||||
|
$2::bytea
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err = result.RowsAffected()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user