Now using the new error handling structure, but need to get an error-messages component working and form validation to do better.

master
Zed A. Shaw 15 hours ago
parent 6f5f3b7143
commit 5e2c34b1bc
  1. 5
      README.md
  2. 23
      features/admin/api.go
  3. 22
      features/admin/db.go
  4. 8
      features/admin/views.go
  5. 22
      features/auth/api.go
  6. 19
      pages/login/index.html
  7. 6
      static/js/code.js
  8. 12
      static/js/jzed.js
  9. 7
      tests/admin/admin_test.go
  10. 11
      views/admin/table/new.html
  11. 26
      views/admin/table/view.html
  12. 11
      views/layouts/main.html

@ -322,7 +322,10 @@ This is just enough code to get a simple JSON API going that we can load from th
you've been editing. To grab the data just do this:
```javascript
const data = await GetJson('/api/myfeature/user_profile');
const [data, status] = await $get_json('/api/myfeature/user_profile');
if($no_error(data, status)) {
// use it
}
```
In place of your prototype data. Doing that will get the data from the `api.go` code you just

@ -3,6 +3,7 @@ package features_admin
import (
"maps"
"reflect"
"fmt"
"github.com/gofiber/fiber/v2"
"MY/webapp/data"
. "MY/webapp/common"
@ -64,17 +65,27 @@ func GetApiSelectOne(c *fiber.Ctx) error {
func PostApiUpdate(c *fiber.Ctx) error {
_, err := AuthCheck(c, true)
if err != nil { return c.Redirect("/") }
if err != nil {
return ApiError(c, "Auth required.")
}
table := c.Params("table")
typeOf := data.Models()[table]
typeOf, ok := data.Models()[table]
if !ok {
return ApiError(c, "Table does not exist")
}
obj, err := ReflectOnPost(typeOf, c)
if err != nil { return IfErrNil(err, c) }
if err != nil {
return ApiError(c, fmt.Sprintf("Invalid format: %v", err))
}
err = Update(table, obj.Elem())
if err != nil { return IfErrNil(err, c) }
id, err := Update(table, obj.Elem())
if err != nil {
return ApiError(c, "Update failed")
}
return c.RedirectBack("/admin/table/", 303)
return c.JSON(fiber.Map{"table": table, "id": id})
}
func GetApiInsert(c *fiber.Ctx) error {

@ -3,13 +3,16 @@ package features_admin
import (
"reflect"
"fmt"
"errors"
"MY/webapp/data"
_ "github.com/mattn/go-sqlite3"
sq "github.com/Masterminds/squirrel"
)
func Schema(table string) []string {
the_type := data.Models()[table]
func Schema(table string) ([]string, error) {
the_type, ok := data.Models()[table]
if !ok { return nil, errors.New("Invalid table") }
field_num := the_type.NumField()
fields := make([]string, 0, field_num)
@ -19,7 +22,7 @@ func Schema(table string) []string {
fields = append(fields, tag)
}
return fields
return fields, nil
}
func SearchTable(search string, table string, limit uint64, page uint64) ([]any, error) {
@ -140,8 +143,9 @@ func Insert(table string, value reflect.Value) (int64, int64, error) {
return id, count, err
}
func Update(table string, value reflect.Value) error {
func Update(table string, value reflect.Value) (int64, error) {
builder := sq.Update(table)
orig_id := value.FieldByName("Id").Int()
type_of := value.Type()
field_num := value.NumField()
@ -156,13 +160,15 @@ func Update(table string, value reflect.Value) error {
builder = builder.Set(tag, field.Interface())
}
builder = builder.Where(sq.Eq{"id": value.FieldByName("Id").Interface()})
builder = builder.Where(sq.Eq{"id": orig_id})
sql_query, args, err := builder.ToSql()
if err != nil { return err }
if err != nil { return -1, err }
fmt.Println("UPDATE QUERY", sql_query, args)
if err != nil { return err}
if err != nil { return -1, err}
_, err = data.DB.Exec(sql_query, args...)
return err
if err != nil { return -1, err }
return orig_id, err
}

@ -25,7 +25,11 @@ func GetPageContent(c *fiber.Ctx) error {
table := c.Params("table")
headers := Schema(table)
headers, err := Schema(table)
if err != nil {
return ApiError(c, "Invalid table")
}
return c.Render("admin/table/contents", fiber.Map{
"table": table,
@ -36,7 +40,7 @@ func GetPageContent(c *fiber.Ctx) error {
func SetupPages(app *fiber.App) {
AddAuthedPage(app, true, "admin/table/", "admin/table/index")
AddAuthedPage(app, true, "admin/table/new/:table/", "admin/table/new")
AddPage(app, "admin/", "admin/index")
AddAuthedPage(app, true, "admin/", "admin/index")
app.Get("admin/table/:table/", GetPageContent)
app.Get("/admin/table/:table/:id/", GetPageSelectOne)

@ -4,8 +4,8 @@ import (
"github.com/gofiber/fiber/v2"
_ "github.com/mattn/go-sqlite3"
sq "github.com/Masterminds/squirrel"
"log"
"fmt"
"MY/webapp/data"
. "MY/webapp/common"
)
@ -44,13 +44,11 @@ func PostApiLogin(c *fiber.Ctx) error {
var user data.User
login, err := ReceivePost[data.Login](c)
if(err != nil) { return IfErrNil(err, c) }
if(err != nil) {
return ApiError(c, "Invalid login form.")
}
pass_good, err := LoginUser(&user, login)
if err != nil {
fmt.Println("!!!!!!!!!!!!!!! YOU SUCK, make this show a form/login error.")
return c.Redirect("/login/")
}
if pass_good {
sess, err := STORE.Get(c)
@ -59,12 +57,18 @@ func PostApiLogin(c *fiber.Ctx) error {
sess.Set("user_id", user.Id)
sess.Set("authenticated", true)
sess.Set("admin", IsAdmin(&user))
err = sess.Save()
if err != nil { return IfErrNil(err, c) }
if err != nil {
return ApiError(c, "Server Failure in Session.")
}
return c.Redirect("/")
return c.JSON(fiber.Map{"valid": true})
} else {
return c.Redirect("/login/")
// custom auth error
if(err != nil) { log.Printf("Error in login: %v", err) }
c.Status(401)
return c.JSON(fiber.Map{"error": "Invalid login."})
}
}

@ -1,5 +1,22 @@
<script>
const login_form = (data, status) => {
if($no_error(data, status)) {
$redirect('/');
}
}
$boot(async () => {
$handle_form('login-form', login_form);
});
</script>
<div id='error'></div>
<template id='error-template'>
<aside id='error'><mark class="alert">${error}</mark></aside>
</template>
<div class="flex flex-col items-center p-6">
<form action="/api/login" method="POST">
<form id='login-form' action="/api/login" method="POST">
<card>
<top><h2 style="color: white">Login</h2></top>
<middle>

@ -67,12 +67,6 @@ class ForeverScroll {
}
}
const GetJson = async (url) => {
const resp = await fetch(url);
console.assert(resp.status == 200, "failed to get it");
return await resp.json();
}
const ConfirmDelete = async (table, obj_id) => {
if(confirm("Are you sure?")) {
await fetch("/api/admin/table/" + table + "/" + obj_id,

@ -299,11 +299,21 @@ const $form_submit = async (form_id, event) => {
}
const $handle_form = (form_id, cb) => {
const form = $id(form_id);
if(!form) {
console.error("There's no form named", form_id);
return;
}
const submitter = async (event) => {
const [data, status] = await $form_submit(form_id, event);
cb(data, status);
}
$id('form').addEventListener('submit', submitter);
form.addEventListener('submit', submitter);
}
const $get_json = async (url) => {
const resp = await fetch(url);
return [await resp.json(), resp.status];
}

@ -64,10 +64,11 @@ func TestAdminIndexPage(t *testing.T) {
result.FieldByName("Username").SetString("joeblow")
result.FieldByName("Email").SetString("what@what.com")
err = admin.Update(table, result)
assert.NoError(err, )
update_id, err := admin.Update(table, result)
assert.NoError(err)
assert.Equal(id.Int(), update_id)
err = admin.Delete(table, id.Int())
err = admin.Delete(table, update_id)
assert.NoError(err)
}
}

@ -1,17 +1,12 @@
<script>
const GetJsonErr = async (url) => {
const resp = await fetch(url);
return [await resp.json(), resp.status];
}
const post_user = (data, status) => {
const post_form = (data, status) => {
if($no_error(data, status)) {
$redirect(`/admin/table/${data.table}/${data.id}`, true);
}
}
$boot(async () => {
let [resp, status] = await GetJsonErr('/api/admin/table/new/{{ .table }}');
let [resp, status] = await $get_json('/api/admin/table/new/{{ .table }}');
if($no_error(resp, status)) {
let data = [];
@ -21,7 +16,7 @@ $boot(async () => {
}
$render_data('data-template', 'data-form', data);
$handle_form('form', post_user);
$handle_form('form', post_form);
}
});
</script>

@ -1,20 +1,34 @@
<script>
$boot(async () => {
let item = await GetJson('/api/admin/table/{{ .table }}/{{ .id }}');
let data = [];
let [item, status] = await $get_json('/api/admin/table/{{ .table }}/{{ .id }}');
for(let key of Object.keys(item)) {
data.push({key, value: item[key]});
const post_form = (data, status) => {
if($no_error(data, status)) {
$redirect(`/admin/table/${data.table}/${data.id}`, true);
}
}
$render_data('data-template', 'data-form', data);
if($no_error(item, status)) {
let data = [];
for(let key of Object.keys(item)) {
data.push({key, value: item[key]});
}
$render_data('data-template', 'data-form', data);
$handle_form('update-form', post_form);
}
});
</script>
<h1><a href="/admin/table/{{ .table }}/">&laquo;</a>Admin {{ .table }}</h1>
<div id='error'></div>
<template id="error-template">
<aside id='error'><mark class="alert">${ error }</mark></aside>
</template>
<block>
<form method="POST" action="/api/admin/table/{{ .table }}/{{ .id }}">
<form id='update-form' method="POST" action="/api/admin/table/{{ .table }}/{{ .id }}">
<card>
<top><h1>{{ .table }} : {{ .id }}</h1></top>
<middle id="data-form">

@ -13,11 +13,14 @@
<title>Go Web Dev Starter Kit</title>
<script>
$boot(async () => {
let auth = await GetJson('/api/authcheck');
let opt = $switch('is-authed-switch', auth.is_authed);
let [auth, status] = await $get_json('/api/authcheck');
if(opt) {
$replace_with($id('login-logout'), opt);
if($no_error(auth, status)) {
let opt = $switch('is-authed-switch', auth.is_authed);
if(opt) {
$replace_with($id('login-logout'), opt);
}
}
});
</script>

Loading…
Cancel
Save