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.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