diff --git a/.gitignore b/.gitignore index bfa6551..9a8e392 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ notes +upload diff --git a/file.go b/file.go index 7f9cfa3..eaafdf8 100644 --- a/file.go +++ b/file.go @@ -12,24 +12,25 @@ import ( type File struct { ID int UserID int `db:"user_id"` + NodeID int Filename string Size int64 MIME string MD5 string Uploaded time.Time - } -func (session Session) AddFile(file *File) (err error) {// {{{ +func (session Session) AddFile(file *File) (err error) { // {{{ file.UserID = session.UserID var rows *sqlx.Rows rows, err = db.Queryx(` - INSERT INTO file(user_id, filename, size, mime, md5) - VALUES($1, $2, $3, $4, $5) + INSERT INTO file(user_id, node_id, filename, size, mime, md5) + VALUES($1, $2, $3, $4, $5, $6) RETURNING id `, file.UserID, + file.NodeID, file.Filename, file.Size, file.MIME, @@ -44,6 +45,28 @@ func (session Session) AddFile(file *File) (err error) {// {{{ err = rows.Scan(&file.ID) fmt.Printf("%#v\n", file) return -}// }}} +} // }}} +func (session Session) Files(nodeID int) (files []File, err error) { // {{{ + var rows *sqlx.Rows + rows, err = db.Queryx( + `SELECT * FROM files WHERE user_id = $1 AND node_id = $2`, + session.UserID, + nodeID, + ) + if err != nil { + return + } + defer rows.Close() + + for rows.Next() { + file := File{} + if err = rows.StructScan(&file); err != nil { + return + } + files = append(files, file) + } + + return +} // }}} // vim: foldmethod=marker diff --git a/main.go b/main.go index 01f5558..5138cbf 100644 --- a/main.go +++ b/main.go @@ -15,13 +15,14 @@ import ( "path" "regexp" "strings" + "strconv" "time" _ "embed" ) const VERSION = "v0.1.2"; const LISTEN_HOST = "0.0.0.0"; -const DB_SCHEMA = 4 +const DB_SCHEMA = 5 var ( flagPort int @@ -358,9 +359,9 @@ func nodeDelete(w http.ResponseWriter, r *http.Request) {// {{{ }) }// }}} func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ + log.Println("/node/upload") var err error var session Session - log.Println("/node/upload") if session, _, err = ValidateSession(r, true); err != nil { responseError(w, err) @@ -369,7 +370,8 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ // Parse our multipart form, 10 << 20 specifies a maximum // upload of 10 MB files. - r.ParseMultipartForm(100 << 20) + r.Body = http.MaxBytesReader(w, r.Body, 128<<20+512) + r.ParseMultipartForm(128 << 20) // FormFile returns the first file for the given key `myFile` // it also returns the FileHeader so we can get the Filename, @@ -381,9 +383,8 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ } defer file.Close() - // Read metadata of file for database, - // and also file contents for MD5, which is used - // to store the file on disk. + // Read metadata of file for database, and also file contents + // for MD5, which is used to store the file on disk. fileBytes, err := io.ReadAll(file) if err != nil { responseError(w, err) @@ -392,7 +393,14 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ md5sumBytes := md5.Sum(fileBytes) md5sum := hex.EncodeToString(md5sumBytes[:]) + + var nodeID int + if nodeID, err = strconv.Atoi(r.PostFormValue("NodeID")); err != nil { + responseError(w, err) + return + } nodeFile := File{ + NodeID: nodeID, Filename: handler.Filename, Size: handler.Size, MIME: handler.Header.Get("Content-Type"), @@ -403,6 +411,10 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ return } + // Files are stored in a directory structure composed of + // the first three characters in the md5sum, which is statistically + // distributed by design, making sure there aren't too many files in + // a single directory. path := filepath.Join( config.Application.Directories.Upload, md5sum[0:1], @@ -428,6 +440,36 @@ func nodeUpload(w http.ResponseWriter, r *http.Request) {// {{{ responseData(w, map[string]interface{}{ "OK": true, + "File": nodeFile, + }) +}// }}} +func nodeFiles(w http.ResponseWriter, r *http.Request) {// {{{ + var err error + var session Session + var files []File + + if session, _, err = ValidateSession(r, true); err != nil { + responseError(w, err) + return + } + + req := struct { + NodeID int + }{} + if err = parseRequest(r, &req); err != nil { + responseError(w, err) + return + } + + files, err = session.Files(req.NodeID) + if err != nil { + responseError(w, err) + return + } + + responseData(w, map[string]interface{}{ + "OK": true, + "Files": files, }) }// }}} diff --git a/sql/0005.sql b/sql/0005.sql new file mode 100644 index 0000000..fe21e55 --- /dev/null +++ b/sql/0005.sql @@ -0,0 +1,2 @@ +ALTER TABLE public.file ADD node_id int4 NOT NULL; +ALTER TABLE public.file ADD CONSTRAINT file_node_fk FOREIGN KEY (node_id) REFERENCES public.node(id) ON DELETE RESTRICT ON UPDATE RESTRICT; diff --git a/static/css/main.css b/static/css/main.css index 4ab270e..ddda497 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -34,7 +34,6 @@ h1 { background: rgba(0, 0, 0, 0.35); } #menu { - display: none; position: absolute; top: 24px; right: 24px; @@ -44,9 +43,6 @@ h1 { box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.5); z-index: 1025; } -#menu.show { - display: initial; -} #menu .item { padding: 16px; border-bottom: 1px solid #aaa; diff --git a/static/js/node.mjs b/static/js/node.mjs index bcfedc0..bed8a16 100644 --- a/static/js/node.mjs +++ b/static/js/node.mjs @@ -52,11 +52,11 @@ export class NodeUI extends Component { let upload = ''; if(this.upload.value) - upload = html`<${UploadUI} app=${this} />` + upload = html`<${UploadUI} nodeui=${this} />` let menu = ''; if(this.menu.value) - upload = html`<${Menu} app=${this} />` + upload = html`<${Menu} nodeui=${this} />` return html` ${menu} @@ -234,6 +234,9 @@ class NodeContent extends Component { }//}}} } +class NodeFiles extends Component { +} + class Node { constructor(app, nodeID) {//{{{ this.app = app @@ -261,14 +264,14 @@ class Node { } class Menu extends Component { - render({ app }) {//{{{ + render({ nodeui }) {//{{{ return html` -