initial commit
This commit is contained in:
		
							parent
							
								
									80cb26c286
								
							
						
					
					
						commit
						4fc9bb5c1f
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -21,3 +21,5 @@
 | 
				
			|||||||
# Go workspace file
 | 
					# Go workspace file
 | 
				
			||||||
go.work
 | 
					go.work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# sqlite3 databases
 | 
				
			||||||
 | 
					*.db
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								cmd/web/handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								cmd/web/handlers.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					    "net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) home(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					    app.render(w, r, http.StatusOK, "home.tmpl.html", templateData{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getUserRegister(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func postUserRegister(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getUsersList(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getUser(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func postGuestbooksCreate(w http.ResponseWriter, r* http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getGuestbooksList(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getGuestbook(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								cmd/web/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								cmd/web/helpers.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					    "net/http"
 | 
				
			||||||
 | 
					    "fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) {
 | 
				
			||||||
 | 
					    var (
 | 
				
			||||||
 | 
					        method = r.Method
 | 
				
			||||||
 | 
					        uri = r.URL.RequestURI()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.logger.Error(err.Error(), "method", method, "uri", uri)
 | 
				
			||||||
 | 
					    http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) clientError(w http.ResponseWriter, status int) {
 | 
				
			||||||
 | 
					    http.Error(w, http.StatusText(status), status)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) render(w http.ResponseWriter, r *http.Request, status int, page string, data templateData) {
 | 
				
			||||||
 | 
					    ts, ok := app.templateCache[page]
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        err := fmt.Errorf("the template %s does not exist", page)
 | 
				
			||||||
 | 
					        app.serverError(w, r, err)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    w.WriteHeader(status)
 | 
				
			||||||
 | 
					    err := ts.ExecuteTemplate(w, "base", data)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        app.serverError(w, r, err)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								cmd/web/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								cmd/web/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"text/template"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
				
			||||||
 | 
						_ "modernc.org/sqlite"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type application struct {
 | 
				
			||||||
 | 
					    logger *slog.Logger
 | 
				
			||||||
 | 
					    templateCache map[string]*template.Template
 | 
				
			||||||
 | 
					    guestbooks *models.GuestbookModel
 | 
				
			||||||
 | 
					    users *models.UserModel
 | 
				
			||||||
 | 
					    guestbookComments *models.GuestbookCommentModel
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
					    addr := flag.String("addr", ":3000", "HTTP network address")
 | 
				
			||||||
 | 
					    dsn := flag.String("dsn", "guestbook.db", "data source name")
 | 
				
			||||||
 | 
					    flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    db, err := openDB(*dsn)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        logger.Error(err.Error())
 | 
				
			||||||
 | 
					        os.Exit(1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    defer db.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    templateCache, err := newTemplateCache()
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        logger.Error(err.Error())
 | 
				
			||||||
 | 
					        os.Exit(1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app := &application{
 | 
				
			||||||
 | 
					        templateCache: templateCache,
 | 
				
			||||||
 | 
					        logger: logger,
 | 
				
			||||||
 | 
					        guestbooks: &models.GuestbookModel{DB: db},
 | 
				
			||||||
 | 
					        users: &models.UserModel{DB: db},
 | 
				
			||||||
 | 
					        guestbookComments: &models.GuestbookCommentModel{DB: db},
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.Info("Starting server on %s", slog.Any("addr", ":4000"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    err = http.ListenAndServe(*addr, app.routes());
 | 
				
			||||||
 | 
					    logger.Error(err.Error())
 | 
				
			||||||
 | 
					    os.Exit(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openDB(dsn string) (*sql.DB, error) {
 | 
				
			||||||
 | 
					    db, err := sql.Open("sqlite", dsn)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return nil, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if err = db.Ping(); err != nil {
 | 
				
			||||||
 | 
					        return nil, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return db, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								cmd/web/routes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								cmd/web/routes.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					    "net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) routes() *http.ServeMux {
 | 
				
			||||||
 | 
					    mux := http.NewServeMux()
 | 
				
			||||||
 | 
					    mux.HandleFunc("/", app.home);
 | 
				
			||||||
 | 
					    return mux
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										51
									
								
								cmd/web/templates.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								cmd/web/templates.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					    "net/http"
 | 
				
			||||||
 | 
					    "path/filepath"
 | 
				
			||||||
 | 
					    "text/template"
 | 
				
			||||||
 | 
					    "time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type templateData struct {
 | 
				
			||||||
 | 
					    CurrentYear int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func humanDate(t time.Time) string {
 | 
				
			||||||
 | 
					    return t.Format("02 Jan 2006 at 15:04")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var functions = template.FuncMap {
 | 
				
			||||||
 | 
					    "humanDate": humanDate,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTemplateCache() (map[string]*template.Template, error) {
 | 
				
			||||||
 | 
					    cache := map[string]*template.Template{}
 | 
				
			||||||
 | 
					    pages, err := filepath.Glob("./ui/html/pages/*.tmpl.html")
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return nil, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for _, page := range pages {
 | 
				
			||||||
 | 
					        name := filepath.Base(page)
 | 
				
			||||||
 | 
					        ts, err := template.New(name).Funcs(functions).ParseFiles("./ui/html/base.tmpl.html")
 | 
				
			||||||
 | 
					        if err != nil {
 | 
				
			||||||
 | 
					            return nil, err
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ts, err = ts.ParseGlob("./ui/html/partials/*.tmpl.html")
 | 
				
			||||||
 | 
					        if err != nil {
 | 
				
			||||||
 | 
					            return nil, err
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ts, err = ts.ParseFiles(page)
 | 
				
			||||||
 | 
					        if err != nil {
 | 
				
			||||||
 | 
					            return nil, err
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cache[name] = ts
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return cache, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *application) newTemplateData(r *http.Request) *templateData {
 | 
				
			||||||
 | 
					    return &templateData {
 | 
				
			||||||
 | 
					        CurrentYear: time.Now().Year(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								db/create-tables-sqlite.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								db/create-tables-sqlite.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					CREATE TABLE users (
 | 
				
			||||||
 | 
					    Id blob(16) primary key,
 | 
				
			||||||
 | 
					    Username varchar(32) NOT NULL,
 | 
				
			||||||
 | 
					    Email varchar(256) NOT NULL,
 | 
				
			||||||
 | 
					    IsDeleted boolean NOT NULL DEFAULT FALSE
 | 
				
			||||||
 | 
					) WITHOUT ROWID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE guestbooks (
 | 
				
			||||||
 | 
					    Id blob(16) primary key,
 | 
				
			||||||
 | 
					    SiteUrl varchar(512) NOT NULL,
 | 
				
			||||||
 | 
					    UserId blob(16) NOT NULL,
 | 
				
			||||||
 | 
					    IsDeleted boolean NOT NULL DEFAULT FALSE,
 | 
				
			||||||
 | 
					    IsActive boolean NOT NULL DEFAULT TRUE,
 | 
				
			||||||
 | 
					    FOREIGN KEY (UserId) REFERENCES users(Id)
 | 
				
			||||||
 | 
					        ON DELETE RESTRICT
 | 
				
			||||||
 | 
					        ON UPDATE RESTRICT
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE guestbook_comments (
 | 
				
			||||||
 | 
					    Id blob(16) primary key,
 | 
				
			||||||
 | 
					    GuestbookId blob(16) NOT NULL,
 | 
				
			||||||
 | 
					    ParentId blob(16),
 | 
				
			||||||
 | 
					    AuthorName varchar(256) NOT NULL,
 | 
				
			||||||
 | 
					    AuthorEmail varchar(256) NOT NULL,
 | 
				
			||||||
 | 
					    AuthorSite varchar(256),
 | 
				
			||||||
 | 
					    CommentText text NOT NULL,
 | 
				
			||||||
 | 
					    PageUrl varchar(256),
 | 
				
			||||||
 | 
					    IsPublished boolean NOT NULL DEFAULT TRUE,
 | 
				
			||||||
 | 
					    IsDeleted boolean NOT NULL DEFAULT FALSE,
 | 
				
			||||||
 | 
					    FOREIGN KEY (GuestbookId) 
 | 
				
			||||||
 | 
					        REFERENCES guestbooks(Id)
 | 
				
			||||||
 | 
					        ON DELETE RESTRICT
 | 
				
			||||||
 | 
					        ON UPDATE RESTRICT,
 | 
				
			||||||
 | 
					    FOREIGN KEY (ParentId)
 | 
				
			||||||
 | 
					        REFERENCES guestbook_comments(Id)
 | 
				
			||||||
 | 
					        ON DELETE RESTRICT
 | 
				
			||||||
 | 
					        ON UPDATE RESTRICT
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
							
								
								
									
										20
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					module git.32bit.cafe/32bitcafe/guestbook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.23.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
 | 
						github.com/google/uuid v1.6.0 // indirect
 | 
				
			||||||
 | 
						github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-isatty v0.0.20 // indirect
 | 
				
			||||||
 | 
						github.com/ncruces/go-strftime v0.1.9 // indirect
 | 
				
			||||||
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
 | 
						golang.org/x/sys v0.22.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
 | 
				
			||||||
 | 
						modernc.org/libc v1.55.3 // indirect
 | 
				
			||||||
 | 
						modernc.org/mathutil v1.6.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/memory v1.8.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/sqlite v1.33.1 // indirect
 | 
				
			||||||
 | 
						modernc.org/strutil v1.2.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/token v1.1.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										29
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 | 
				
			||||||
 | 
					github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 | 
				
			||||||
 | 
					github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
				
			||||||
 | 
					github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
 | 
					github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
 | 
				
			||||||
 | 
					github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
 | 
					github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
 | 
				
			||||||
 | 
					github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
 | 
				
			||||||
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 | 
				
			||||||
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
				
			||||||
 | 
					modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
 | 
				
			||||||
 | 
					modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
 | 
				
			||||||
 | 
					modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
 | 
				
			||||||
 | 
					modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
 | 
				
			||||||
 | 
					modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
 | 
				
			||||||
 | 
					modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
 | 
				
			||||||
 | 
					modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
 | 
				
			||||||
 | 
					modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
 | 
				
			||||||
 | 
					modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
 | 
				
			||||||
 | 
					modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
 | 
				
			||||||
 | 
					modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
 | 
				
			||||||
 | 
					modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
 | 
				
			||||||
 | 
					modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
 | 
				
			||||||
 | 
					modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 | 
				
			||||||
							
								
								
									
										47
									
								
								internal/models/guestbook.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/models/guestbook.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/uuid"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Guestbook struct {
 | 
				
			||||||
 | 
					    ID uuid.UUID
 | 
				
			||||||
 | 
					    SiteUrl string
 | 
				
			||||||
 | 
					    UserId uuid.UUID
 | 
				
			||||||
 | 
					    IsDeleted bool
 | 
				
			||||||
 | 
					    IsActive bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GuestbookModel struct {
 | 
				
			||||||
 | 
					    DB *sql.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookModel) Insert(siteUrl string, userId uuid.UUID) (uuid.UUID, error) {
 | 
				
			||||||
 | 
					    id := uuid.New()
 | 
				
			||||||
 | 
					    stmt := `INSERT INTO guestbooks (Id, SiteUrl, UserId, IsDeleted, IsActive)
 | 
				
			||||||
 | 
					    VALUES(?, ?, FALSE, TRUE)`
 | 
				
			||||||
 | 
					    _, err := m.DB.Exec(stmt, id, siteUrl, userId)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return uuid.UUID{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return id, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookModel) Get(id uuid.UUID) (Guestbook, error) {
 | 
				
			||||||
 | 
					    stmt := `SELECT Id, SiteUrl, UserId, IsDeleted, IsActive FROM guestbooks
 | 
				
			||||||
 | 
					    WHERE id = ?`
 | 
				
			||||||
 | 
					    row := m.DB.QueryRow(stmt, id)
 | 
				
			||||||
 | 
					    var g Guestbook
 | 
				
			||||||
 | 
					    err := row.Scan(&g.ID, &g.SiteUrl, &g.UserId, &g.IsDeleted, &g.IsActive)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return Guestbook{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return g, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookModel) GetAll(userId uuid.UUID) ([]Guestbook, error) {
 | 
				
			||||||
 | 
					    return []Guestbook{}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								internal/models/guestbookcomment.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								internal/models/guestbookcomment.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/uuid"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GuestbookComment struct {
 | 
				
			||||||
 | 
					    ID uuid.UUID
 | 
				
			||||||
 | 
					    GuestbookId uuid.UUID
 | 
				
			||||||
 | 
					    ParentId uuid.UUID
 | 
				
			||||||
 | 
					    AuthorName string
 | 
				
			||||||
 | 
					    AuthorEmail string
 | 
				
			||||||
 | 
					    AuthorSite string
 | 
				
			||||||
 | 
					    CommentText string
 | 
				
			||||||
 | 
					    PageUrl string
 | 
				
			||||||
 | 
					    IsPublished bool
 | 
				
			||||||
 | 
					    IsDeleted bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GuestbookCommentModel struct {
 | 
				
			||||||
 | 
					    DB *sql.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookCommentModel) Insert(guestbookId, parentId uuid.UUID, authorName,
 | 
				
			||||||
 | 
					    authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (uuid.UUID, error) {
 | 
				
			||||||
 | 
					    id := uuid.New()
 | 
				
			||||||
 | 
					    stmt := `INSERT INTO guestbook_comments (Id, GuestbookId, ParentId, AuthorName,
 | 
				
			||||||
 | 
					    AuthorEmail, AuthorSite, CommentText, PageUrl, IsPublished, IsDeleted)
 | 
				
			||||||
 | 
					    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE)`
 | 
				
			||||||
 | 
					    _, err := m.DB.Exec(stmt, id, guestbookId, parentId, authorName, authorEmail,
 | 
				
			||||||
 | 
						authorSite, commentText, pageUrl, isPublished)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
						return uuid.UUID{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return id, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookCommentModel) Get(id uuid.UUID) (GuestbookComment, error) {
 | 
				
			||||||
 | 
					    stmt := `SELECT Id, GuestbookId, ParentId, AuthorName, AuthorEmail, AuthorSite,
 | 
				
			||||||
 | 
					    CommentText, PageUrl, IsPublished, IsDeleted FROM guestbook_comments WHERE id = ?`
 | 
				
			||||||
 | 
					    row := m.DB.QueryRow(stmt, id)
 | 
				
			||||||
 | 
					    var c GuestbookComment
 | 
				
			||||||
 | 
					    err := row.Scan(&c.ID, &c.GuestbookId, &c.ParentId, &c.AuthorName, &c.AuthorEmail, &c.AuthorSite, &c.CommentText, &c.PageUrl, &c.IsPublished, &c.IsDeleted)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
						return GuestbookComment{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return c, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *GuestbookCommentModel) GetAll(guestbookId *uuid.UUID) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								internal/models/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								internal/models/user.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/uuid"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type User struct {
 | 
				
			||||||
 | 
					    ID uuid.UUID
 | 
				
			||||||
 | 
					    Username string
 | 
				
			||||||
 | 
					    Email string
 | 
				
			||||||
 | 
					    IsDeleted bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UserModel struct {
 | 
				
			||||||
 | 
					    DB *sql.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *UserModel) Insert(username string, email string) (uuid.UUID, error) {
 | 
				
			||||||
 | 
					    id := uuid.New()
 | 
				
			||||||
 | 
					    stmt := `INSERT INTO users (Id, Username, Email, IsDeleted)
 | 
				
			||||||
 | 
					    VALUES (?, ?, ?, FALSE)`
 | 
				
			||||||
 | 
					    _, err := m.DB.Exec(stmt, id, username, email)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
						return uuid.UUID{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return id, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *UserModel) Get(id uuid.UUID) (User, error) {
 | 
				
			||||||
 | 
					    stmt := `SELECT Id, Username, Email, IsDeleted FROM users WHERE id = ?`
 | 
				
			||||||
 | 
					    row := m.DB.QueryRow(stmt, id)
 | 
				
			||||||
 | 
					    var u User
 | 
				
			||||||
 | 
					    err := row.Scan(&u.ID, &u.Username, &u.Email, &u.IsDeleted)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
						return User{}, err
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return u, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								ui/html/base.tmpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								ui/html/base.tmpl.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					{{ define "base" }}
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					    <head>
 | 
				
			||||||
 | 
					        <title>{{ template "title" }} - Guestbook</title>
 | 
				
			||||||
 | 
					        <meta charset="UTF-8">
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
				
			||||||
 | 
					        <link href="css/style.css" rel="stylesheet">
 | 
				
			||||||
 | 
					    </head>
 | 
				
			||||||
 | 
					    <body>
 | 
				
			||||||
 | 
					        <header>
 | 
				
			||||||
 | 
					            <h1><a href="/">Guestbook</a></h1>
 | 
				
			||||||
 | 
					        </header>
 | 
				
			||||||
 | 
					        {{ template "nav" . }}
 | 
				
			||||||
 | 
					        <main>
 | 
				
			||||||
 | 
					            {{ template "main" . }}
 | 
				
			||||||
 | 
					        </main>
 | 
				
			||||||
 | 
					        <footer>
 | 
				
			||||||
 | 
					            <p>A 32-bit Cafe Project</p>
 | 
				
			||||||
 | 
					        </footer>
 | 
				
			||||||
 | 
					    </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
 | 
					{{ end }}
 | 
				
			||||||
							
								
								
									
										5
									
								
								ui/html/pages/home.tmpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/html/pages/home.tmpl.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{{ define "title" }}Home{{ end }}
 | 
				
			||||||
 | 
					{{ define "main" }}
 | 
				
			||||||
 | 
					<h2>Latest Guestbooks</h2>
 | 
				
			||||||
 | 
					<p>There's nothing here yet</p>
 | 
				
			||||||
 | 
					{{ end }}
 | 
				
			||||||
							
								
								
									
										5
									
								
								ui/html/partials/nav.tmpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/html/partials/nav.tmpl.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{{ define "nav" }}
 | 
				
			||||||
 | 
					<nav>
 | 
				
			||||||
 | 
					    <a href="/">Home</a>
 | 
				
			||||||
 | 
					</nav>
 | 
				
			||||||
 | 
					{{ end }}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user