diff --git a/README.md b/README.md
index 3cf16ff..6b264b6 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,535 @@
# Go Web Starter Kit
-This is a fairly complete web development starter kit in Go. It tries to
-be as simple as possible without leaving out modern features like reactive UIs and database
-migrations. A primary thing that's included is working authentication, since that's the main thing
-holding people back when they first start, and also the easiest to get wrong.
+This is a fairly complete web development starter kit in Go. It tries to be as simple as possible
+without leaving out modern features. The goal is to create a setup that a normal person can learn
+and study core web technologies like the DOM and web servers. I would classify it as, "Old school
+with modern things."
-In fact, if you look at how I do it in this first version it is _WRONG_ so do not use this in
-production yet until I can make it correct. Just use it to learn for now.
+## The Stack
+
+Currently I'm using the following components in a tastefully crafted stack similar to a Crepe Cake:
+
+* [goose](https://github.com/pressly/goose) -- This manages the database migrations.
+* [sqlx](https://github.com/jmoiron/sqlx) -- This is the database driver.
+* [squirrel](https://github.com/Masterminds/squirrel) -- This is the SQL query generator.
+* [Fiber](https://gofiber.io/) -- This is the main web API and does most everything you need.
+* [tailwind](https://tailwindcss.com/) -- This makes your sites perty and is easy to use, but I
+ reject the `@apply` slander and use `@apply` to give you nice things.
+* [ssgod](https://lcthw.dev/go/ssgod) -- A static site generator I wrote that _only_ does static site generation and nothing else...unlike Hugo.
+* [ozai](https://lcthw.dev/go/ozai) -- A process manager that replaced Air because Air went crazy like Hugo.
+* [chromedp](https://github.com/chromedp/chromedp) -- This does your automated testing for you.
+
+I then add a few little "sweetener" APIs on top of this for simple things like, making the tests
+nicer or making it easier to quickly return a page from a view.
## Getting Started
-Programmers hate duplication so if you want the instructions read the [index.md file in
-pages](pages/index.md).
+First, install a couple tools that'll be used everywhere, and that have huge dependencies you don't
+actually need in your project:
+
+```shell
+go install golang.org/x/pkgsite/cmd/pkgsite@latest
+go install github.com/pressly/goose/v3/cmd/goose@latest
+```
+
+To start your own project do this:
+
+```shell
+git clone https://lcthw.dev/go/go-web-starter-kit.git my-project
+cd my-project
+```
+
+Then on Linux/OSX you want to delete the `.git` with this:
+
+```shell
+rm -rf .git
+mv LICENSE LICENSE.old # it's MIT
+```
+
+And on Windows you use the ever more clear and totally easier:
+
+```shell
+rm -recurse -force .git
+mv LICENSE LICENSE.old # it's MIT
+```
+
+Once you have that you can make it your own:
+
+```shell
+git init .
+go mod tidy
+cp config_example.json config.json
+make migrate_up
+sqlite3 db.sqlite3 ".read tools/pragmas.sql"
+make
+make dev
+```
+
+This gets the site running and ready for development. You only need _to do this once._. The next
+day that you want to work, cd to your project and only run `make dev`:
+
+```shell
+make dev
+```
+
+## Linux Bug?
+
+I found that on Ubuntu the `tailwindcss` command "wouldn't run." Turns out when `tailwindcss` forks
+it does the right thing and closes all possible open file descriptors, _but_ Ubuntu has set the hard
+limit to 1 billion:
+
+```shell
+$ ulimit -Hn
+1073741816
+```
+
+This is a _lot_ of files to try to close, so `tailwindcss` is actually stuck doing that. You can
+fix this with:
+
+```shell
+ulimit -n 65536
+```
+
+You can pick any reasonable number, and then `tailwindcss` works like expected.
+
+## Configuration
+
+There's 3 basic moving parts to the whole system: `webapp`, `ssgod`, `tailwind`, and `ozai`.
+
+You can configure the `webapp` using the `config.json` which you copied over from `config_example.json`. The options are fairly self-explanatory.
+
+You can configure [ssgod](https://lcthw.dev/go/ssgod) using the
+`ssgod.json` file. You'll notice that there's _two_ layouts with this setup, one for `webapp`
+and another for `ssgod`. You can just point `ssgod` at your `views/layouts/main.html` file if you
+want, but it seems people want two different layouts for their static site vs. "the app."
+
+You can configure `tailwind` using the `static/input_style.css` and editing the `Makefile` to have
+it run how you want. I use the `@apply` heavily because I'm a programmer and no programmer would
+ever tell someone they should sprinkle repetitive bullshit all over their HTML. Resist the
+authority. Use `@apply`.
+
+Finally, you can configure other processes to run with [Ozai](https://lcthw.dev/go/ozai) in the
+`.ozai.json` file. Why so many different formats? I'm still learning which is best to use and while
+I like TOML I think I may go with `.json` files to reduce dependencies. You tell me though.
+
+## Ozai Autorestarts
+
+Ozai is a simpler tool that doesn't do auto restart. Instead it will create a little webserver at
+`http://127.0.0.1:9999` with endpoints you can hit with curl to force a restart. Look in the
+.ozai.json file to see what's configured. This makes it easy to simple add a `curl` call in your
+build process and kick everything over. Look in the `Makefile` for how I do this.
+
+This works better than the way `Air` does it for a few reasons:
+
+1. You can run your build like normal in your own IDE or build tool and see your errors directly.
+ When you use other autobuild tools they have the errors so you can't access them.
+2. You get an immediate restart after your build succeeds, rather than waiting for an external tool
+ to detect you made changes, run the build, and then restart your browser.
+3. You avoid the "auto reload deadlock" where you run a build, the server is shutdown, but you hit
+ refresh too soon, so now your browser waits for an error, then you have to refresh again. Way
+easier to just not use any of that.
+4. No more silent failures hiding inside your terminal that you can't see. Just manually restart
+ it.
+
+For more, checkout the [Ozai Project](https://lcthw.dev/go/ozai").
+
+## Tour of Directories
+
+The directories are organized in a way that separates "App Stuff" from "Content Stuff".
+
+* `admin` App The code for the built-in database admin tool.
+* `bin` App Binaries that run your app like `webapp`.
+* `features` App Where you put your features.
+* `common`App Common helper functions.
+* `config` App The configuration for your site, which is `config.json` loaded into Go structs.
+* `data` App Your database stuff. You put your SQL models in here, and then add them to the bottom `Map` to
+make them magically show up in the `/admin/table/` tool.
+* `migrations` App When you use [goose](https://github.com/pressly/goose) it puts the migrations in here.
+* `tests` App Your tests go in here. I use [chromedp](https://github.com/chromedp/chromedp) but the `chromedp`
+API is too low level (and weird) to be useful so look in `tests/tools.go` for what I use.
+* `tools` App Various admin automation tools go here.
+* `views` Content This is where you put the _App_ contents, not the static content. Sadly, this
+ should be in `features/` but Fiber makes that too difficult.
+* `emails` Content This where you put templates for emails you'll send.
+* `pages` Content These are templates that are generated once and then served statically.
+* `static` Static This is static content like `.js`, images, and `.css`.
+
+The following directories are considered "build junk" and you _should not_ commit them to your git.
+
+* `public` Junk This is generated by `ssgod` and then served by your `webapp`. Don't
+ edit anything in here, and instead edit in `pages` or `static`.
+* `tmp` Junk This is made during the build/testing process.
+
+## Dev Workflow
+
+The first thing I do is run each of these commands in different terminals, either with tmux or just
+using my fingers to run multiple tabs in PowerShell:
+
+```shell
+make dev
+```
+
+### 1. Create a Feature
+
+The first step is to create a `feature`, which is nothing more than some files in a directory that
+you edit to make your `pages/` actually work. You can create a feature like this:
+
+```shell
+./bin/fgen -name myfeatue
+```
+
+This will create the files and directories you need to make a new feature:
+
+`features/myfeature/`
+: This contains all of the go code.
+
+`views/myfeature/`
+: This is where your HTML goes.
+
+`tests/myfeature/`
+: This is where your tests go.
+
+Next you add it to `features/init.go` to enable it:
+
+```diff
+@@ -8,2 +8,3 @@ import (
+ "MY/webapp/features/fakepay"
++ "MY/webapp/features/myfeature"
+ )
+@@ -15,2 +16,3 @@ func Setup(app *fiber.App) {
+ features_fakepay.Setup(app)
++ features_myfeature.Setup(app)
+ }
+```
+
+> __NOTE__: I'm going to automate this in the future, so expect this file to be generated.
+
+After that run `make` to build everything again and you should have a new feature at
+`http://127.0.0.1:7001/myfeature/`
+with your browser.
+
+### 2. Create Initial Views
+
+The design of this system is that you do _not_ have to edit 16 files just get an idea going. You
+can have an idea and create a single `.html` or `.md` file in `views/myfeature` to see it happen. This lets
+you work out all of the URLs, page flows, layouts, and basic design before writing any more code.
+
+This is generally a better way to work than creating the backend models first as it constrains what
+you create to only exactly what you need for the UI you'll implement.
+
+What I do is make a new file in `myfeature/views/`, cook up a quick 2D layout that's mostly what I want, then
+make it link to other `myfeature/views/` to figure out the feature. Once I have a rough UI for the feature
+worked out I move on to the next step.
+
+### Using the Tailwind Starter
+
+I've included [tailwind](https://tailwindcss.com/) in this setup, since it's decent at getting
+things working quickly right in the HTML. The only thing I've fully rejected is the idea that
+"@apply is bad m'kay." No, `@apply` makes it so I don't rage at all the idiots who think 92
+repeated CSS classes on every `div` is "good practice."
+
+> __NOTE__ You can view a sample page with all of these in [/examples/sample.html](/examples/sample.html) and you can look at the other files in `examples` to get an idea of what you can do. Some of these just use tailwind, others use my starter kit.
+
+In my setup there's a `static/input_style.css` that is used to generate a `static/style.css` and it
+comes preconfigured with many of the things you'll need to get a page up and running. In theory
+you can prototype a first page with just HTML and a bit of tailwind as needed. It also uses many of
+the stock HTML tags like `aside` and `mark` so you can keep things nice.
+
+I've added 4 additional layout tags for you to use:
+
+* `grid` -- For stuff in a grid. Add `class="grid-cols-2"` from tailwind to change its layout.
+* `block` -- A simple pre-configured vertical flex box. Put it around vertical stuff.
+* `bar` -- A simple pre-configured horizontal flex box. Pit it around horizontal stuff.
+* `stack` -- A simple way to stack stuff on top of other stuff. You know, like in every single 2d compositional system since the 1500s.
+* `shape` -- A simple 2d box to use for placeholders where you'll put images. It's a lot quicker to drop a little `shape` than to go find an image. I think it also makes it easier to focus on the design.
+
+
+### 3. Create Fake Data
+
+Create a fake set of data that comes from your page prototype. For example, if I have a User
+Profile page then I might add this:
+
+```html
+
+```
+
+This will give you something to work with while you sort out how the interactive parts of your page
+will work. If you don't have any then you can skip this part.
+
+### 4. Refine the UI Interactions
+
+Using the fake data determine the user interactions for the view. Remember that this system is
+designed to minimize the amount of work you need on the front-end, so all you're doing here is
+simple things like validating a form, getting data, disabling/enabling fields, and anything that
+stays _on one page_. If you find yourself trying to cram multiple pages into one then you've gone
+too far.
+
+With my simple use-profile example I could create the function that renders the table on page load:
+
+```javascript
+$boot(async() => {
+ const tmpl = $id('table-row');
+ const target = $id('table-items');
+
+ for(let key in data) {
+ $append(target,
+ $render(tmpl, {key, value: data[key]}));
+ }
+});
+```
+
+This comes from my `jzed.js` file that's a minimalist old-school DOM helper. I also include
+[lit.js](https://lit.dev/) if you prefer to create little components for things.
+
+### 5. Move the Fake Data to `api.go`
+
+You should be spending a lot of time working out the data and interactions in the nice convenience
+of the page you're working on, but eventually that data needs to come from the server. To continue
+the _User Profile_ example we can create a handler in `api.go` that returns this same fake data:
+
+```go
+package features_myfeature
+
+import (
+ "github.com/gofiber/fiber/v2"
+)
+
+type UserProfile struct {
+ UserName string
+ Email string
+}
+
+func GetApiUserProfile(c *fiber.Ctx) error {
+ profile := UserProfile{
+ UserName: "Zed",
+ Email: "zed@zed.com",
+ }
+
+ return c.JSON(profile)
+}
+
+func SetupApi(app *fiber.App) {
+ app.Get("/api/myfeature/user_profile", GetApiUserProfile)
+}
+```
+
+This is just enough code to get a simple JSON API going that we can load from the `index.html`
+you've been editing. To grab the data just do this:
+
+```javascript
+ const data = await GetJson('/api/myfeature/user_profile');
+```
+
+In place of your prototype data. Doing that will get the data from the `api.go` code you just
+wrote.
+
+### 6. Store the Data in `db.go`
+
+Once you've sorted out how the data is going to come from your API you can start to store it in the
+database. The first step for that is to move your data into `db.go`:
+
+```go
+package features_myfeature
+
+type UserProfile struct {
+ UserName string
+ Email string
+}
+
+func GetUserProfile(id int) (UserProfile, error) {
+ profile := UserProfile{
+ UserName: "John",
+ Email: "zed@zed.com",
+ }
+
+ return profile, nil
+}
+```
+
+The `UserProfile` is removed from `api.go` and placed in `db.go`. You then call your
+`GetUserProfile` function to get it:
+
+```go
+import (
+ "github.com/gofiber/fiber/v2"
+ . "MY/webapp/common"
+)
+
+func GetApiUserProfile(c *fiber.Ctx) error {
+ profile, err := GetUserProfile(1)
+ if err != nil { return IfErrNil(err, c) }
+
+ return c.JSON(profile)
+}
+```
+
+> __NOTE__: This is intended as a simplified way to _grow_ a feature from idea to working backend.
+> Eventually you would want to create a separate module for your data models, create your tests for
+> them, and other important design considerations.
+
+### 7. Move Hard Stuff to `views.go`
+
+There's some things that are too hard to do in JavaScript, but are very easy to do in `views.go`
+usign the basic templates available. An example of this is defining the structure of a `
`
+tag. Doing this in JavaScript is a massive pain, but doing it with a template in `views.go` is
+easy. Another example would be setting the `id` for some data object the page should display. It's
+far easier to do that right in the template.
+
+I won't get into how this is done, but look at any of the examples in `features/` and `admin/` to
+see me do this.
+
+### 8. Put it In The Database
+
+Once you have your data API worked out you can then use
+[squirrel](https://github.com/Masterminds/squirrel), [goose](https://github.com/pressly/goose), and
+[sqlx](https://github.com/jmoiron/sqlx) to make it store everything in the database.
+
+First you create a migration for your data:
+
+```shell
+goose sqlite3 db.sqlite3 create create_user_profile sql -dir migrations
+```
+
+You then edit the `_create_user_profile.sql` file to create the new table:
+
+```sql
+-- +goose Up
+-- +goose StatementBegin
+CREATE TABLE user_profile (
+ username TEXT
+ email TEXT
+);
+-- +goose StatementEnd
+
+-- +goose Down
+-- +goose StatementBegin
+DROP TABLE user_profile;
+-- +goose StatementEnd
+```
+
+You use `goose` to migrate the database up:
+
+```shell
+goose sqlite3 db.sqlite3 -dir migrations up
+```
+
+If you make a mistake, you can migrate "down" to undo your last migration:
+
+```shell
+goose sqlite3 db.sqlite3 -dir migrations down
+```
+
+I also have convenient `Makefile` actions for this:
+
+```shell
+make migrate_up
+make migrate_down
+```
+
+### 9. Rewrite `db.go` to Store It
+
+The next step needs refinement, so I'll only briefly describe it now:
+
+1. You have your data, so annotate it with `db: ""` struct tags.
+2. Use the `squirrel` and `sqlx` APIs to store it.
+3. Add your usual CRUD operations.
+
+I'm going to improve this so most of this can be automated, so stay tuned. For an example of doing
+this look in `admin/table/db.go`.
+
+### 10. Refine, Refine, Refine
+
+Finally, nothing in this process is meant to be a lock-step one and done operation. This is simply a
+mechanism to help people go from abstract idea to concrete working code with all the stages
+necessary. Once you have everything "working" you'll need to refine it. The key to refinement is to
+think about how this feature works with other features:
+
+1. Are you repeating data that some other feature has? Just use that feature's data operations then.
+2. Are you repeating components that another feature has? Try making a `lit.js` component you both use.
+3. Should you move the data used by many features into a single place? Look at `data/model.go`.
+
+You should also go over the API and page interactions in your feature, and when it's good move onto
+the next feature. The only way to learn this step is to do it a lot, but in general, less is more.
+If you've got 10k lines of code in a feature then rethink it.
+
+## Note on The Process for Pros
+
+This process is mostly for people who are learning how to build a web application. Once you've done
+it this way for a few web apps then you can do it however works best for your idea.
+
+Some people's ideas are about the data, so starting with the UI like this may not work. If your
+idea is about the data, start with the `api.go` and `db.go`, and then work the tests.
+
+Generally though, I've found that people who start with the backend are mostly only starting there
+because that's what they know. They also tend to create glorious crystal palaces that are
+completely unnecessary because they aren't grounded in the reality of a UI actual people need to
+use.
+
+When you start with the UI can you can prototype all of the interactions, figure out the data, and
+aim for only what you need. Keep doing this and you'll slowly build a nice data model with less
+cruft for the application you're creating.
+
+
+## Additional Features
+
+### Sending Emails
+
+Coming soon...
+
+### Receiving Payments
+
+Coming soon...
+
+## FAQ
+
+Here's some questions I get when people see this.
+
+### Isn't Fiber Banned?!
+
+First off, nobody has the right to "ban" a project. If you're hanging out in the Go discord and
+you've seen Fiber on some ban list then whoever did that is a fucking asshole. Just ignore that
+bullshit and use the best tech you can.
+
+Second, if you're only supposed to use the Go standard library then why [is there a whole fucking
+document on the official go website explaining how to use Gin to make an API?](https://go.dev/doc/tutorial/web-service-gin). It seems like whoever is claiming that you are only allowed to use the Go standard library is in disagreement with the _actual fucking Go project_.
+
+Finally, you don't have to do what you're told, especially when the person telling you what to do is
+some random asshole hiding in a chat room being a random asshole. Use what you want, and Fiber is
+in that sweet spot of being a nice API that isn't slow dogshit like [Gin](https://www.techempower.com/benchmarks/).
+
+### Why Not Gin?
+
+Gin is a piece of crap, that's why. Look at the [Techempower Benchmarks](https://www.techempower.com/benchmarks/) to get an idea of how bad Gin is. It's at the _bottom_ of the list, performing even _worse_ that some of the slowest _Ruby_ web frameworks out there. Make a _compiled language_ like Go perform that bad takes a special kind of incompetence.
+
+### Why Not Only the Go Standard Library?
+
+Because I like nice things, and while the Go Standard Library has everything you _need_, it
+frequently is missing things you _want_. This project is about adding the things you'll probably
+want without getting to abstract and far away from learning the core web technologies.
+
+### Are Your Tags "Accessible"?! What about SEO?!
+
+Yes, I actually tested all of this with [NV Access](https://www.nvaccess.org/) and the only thing NV
+Access seems to have a problem with is the `pre` and `code` tags. That's because apparently those
+are deemed as "images" so they get ignored...which is about the dumbest fucking shit I've ever
+heard. I'm working on a way to fix that.
+
+Anyone telling you that my `grid`, `bar`, `block`, or `stack` tags impact accessibility most likely
+has never ran NV Access one time so they're just wrong. Additionally, it's better to have these be
+_ignored_ by screen readers because they're just 2D layout junk, so they're kind of irrelevant to a
+linear 1D screen reader.
+
+Finally, SEO isn't impacted because Google has literally said HTML quality doesn't matter for SEO
+ranking. Think about it, if Google went around dictating exact HTML they would get in so much
+Monopoly trouble. Instead they tend to focus more on content and organization, and I believe the
+official quote is, "Making everyone use the same HTML would make the web boring."
+
+However, take this with a grain of salt because the same Google people who've said this also said
+that backlinks didn't impact performance and many SEO experts say this is totally not true. Do your
+own testing, and if it impacts your SEO than just change them after you get your initial design up.
+
diff --git a/common/email/api.go b/common/email/api.go
index a587cc3..77f5119 100644
--- a/common/email/api.go
+++ b/common/email/api.go
@@ -97,6 +97,7 @@ func (router *Router) DeliverEmail(msg EmailMessage) error {
email_msg.SetGenHeader(mail.HeaderUserAgent, config.Settings.Email.UserAgent)
}
+ log.Println("template is", msg.Template)
text, html, err := router.Render(msg.Template, nil)
if err != nil { return err }
@@ -135,6 +136,8 @@ func (router *Router) HandleEmailRequest() error {
}
func (sender *Sender) QueueEmail(msg EmailMessage) error {
+
+ log.Println("email msg", msg)
msg_json, err := json.Marshal(msg)
if err != nil { return err }
diff --git a/features/init.go b/features/init.go
index a283162..ac71ee2 100644
--- a/features/init.go
+++ b/features/init.go
@@ -6,6 +6,7 @@ import (
"MY/webapp/features/paypal"
"MY/webapp/features/shopping"
"MY/webapp/features/fakepay"
+ "MY/webapp/features/myfeature"
)
func Setup(app *fiber.App) {
@@ -13,4 +14,5 @@ func Setup(app *fiber.App) {
features_paypal.Setup(app)
features_shopping.Setup(app)
features_fakepay.Setup(app)
+ features_myfeature.Setup(app)
}
diff --git a/pages/index.md b/pages/index.md
index 93bf624..732dcce 100644
--- a/pages/index.md
+++ b/pages/index.md
@@ -1,267 +1,4 @@
-# Go Web Starter Kit
+# Welcome!
-This is a fairly complete web development starter kit in Go. It tries to
-be as simple as possible without leaving out modern features like reactive UIs and database
-migrations. A primary thing that's included is working authentication, since that's the main thing
-holding people back when they first start, and also the easiest to get wrong.
-
-In fact, if you look at how I do it in this first version it is _WRONG_ so do not use this in
-production yet until I can make it correct. Just use it to learn for now.
-
-## The Stack
-
-Currently I'm using the following components in a tastefully crafted stack similar to a Crepe Cake:
-
-* [goose](https://github.com/pressly/goose) -- This manages the database migrations.
-* [sqlx](https://github.com/jmoiron/sqlx) -- This is the database driver.
-* [squirrel](https://github.com/Masterminds/squirrel) -- This is the SQL query generator.
-* [Fiber](https://gofiber.io/) -- This is the main web API and does most everything you need.
-* [tailwind](https://tailwindcss.com/) -- This makes your sites perty and is easy to use, but I
- reject the `@apply` slander and use `@apply` to give you nice things.
-* [Alpine.js](https://alpinejs.dev/) -- This gives you just enough reactivity to not be annoyed, but
- not so much that you hate the web.
-* [ssgod](https://lcthw.dev/go/ssgod) -- A static site
- generator I wrote that _only_ does static site generation.
-* [chromedp](https://github.com/chromedp/chromedp) -- This does your automated testing for you.
-
-I then add a few little "sweetener" APIs on top of this for simple things like, making the tests
-nicer or making it easier to quickly return a page from a view.
-
-### Isn't Fiber Banned?!
-
-First off, nobody has the right to "ban" a project. If you're hanging out in the Go discord and
-you've seen Fiber on some ban list then whoever did that is a fucking asshole. Just ignore that
-bullshit and use the best tech you can.
-
-Second, if you're only supposed to use the Go standard library then why [is there a whole fucking
-document on the official go website explaining how to use Gin to make an API?](https://go.dev/doc/tutorial/web-service-gin). It seems like whoever is claiming that you are only allowed to use the Go standard library is in disagreement with the _actual fucking Go project_.
-
-Finally, you don't have to do what you're told, especially when the person telling you what to do is
-some random asshole hiding in a chat room being a random asshole. Use what you want, and Fiber is
-in that sweet spot of being a nice API that isn't slow dogshit like [Gin](https://www.techempower.com/benchmarks/).
-
-## Getting Started
-
-First, install a couple tools that'll be used everywhere, and that have huge dependencies you don't
-actually need in your project:
-
-```shell
-go install golang.org/x/pkgsite/cmd/pkgsite@latest
-go install github.com/pressly/goose/v3/cmd/goose@latest
-```
-
-You can get use this project working by doing this:
-
-```shell
-git clone https://lcthw.dev/go/go-web-starter-kit.git my-project
-cd my-project
-```
-
-Then on Linux/OSX you want to delete the `.git` with this:
-
-```shell
-rm -rf .git
-mv LICENSE LICENSE.old # it's MIT
-```
-
-And on Windows you use the ever more clear and totally easier:
-
-```shell
-rm -recurse -force .git
-mv LICENSE LICENSE.old # it's MIT
-```
-
-Once you have that you can make it your own:
-
-```shell
-git init .
-go mod tidy
-cp config_example.toml config.toml
-make migrate_up
-sqlite3 db.sqlite3 ".read tools/pragmas.sql"
-make build
-make dev
-```
-
-This gets the site running and ready for development. You only need _to do this once._. After
-this, when you're ready to work just do:
-
-```shell
-make dev
-```
-
-## Linux Bug?
-
-I found that on Ubuntu the `tailwindcss` command "wouldn't run." Turns out when `tailwindcss` forks
-it does the right thing and closes all possible open file descriptors, _but_ Ubuntu has set the hard
-limit to 1 billion:
-
-```shell
-$ ulimit -Hn
-1073741816
-```
-
-This is a _lot_ of files to try to close, so `tailwindcss` is actually stuck doing that. You can
-fix this with:
-
-```shell
-ulimit -n 65536
-```
-
-You can pick any reasonable number, and then `tailwindcss` works like expected.
-
-## Configuration
-
-There's 3 basic moving parts to the whole system: `webapp.exe`, `ssgod`, `tailwind`, and `ozai`.
-
-You can configure the `webapp.exe` using the `config.toml` which you copied over from `config_example.toml`. The options are fairly self-explanatory.
-
-You can configure [ssgod](https://lcthw.dev/go/ssgod) using the
-`ssgod.toml` file. You'll notice that there's _two_ layouts with this setup, one for `webapp.exe`
-and another for `ssgod`. You can just point `ssgod` at your `views/layouts/main.html` file if you
-want, but it seems people want two different layouts for their static site vs. "the app."
-
-You can configure `tailwind` using the `static/input_style.css` and editing the `Makefile` to have
-it run how you want. I use the `@apply` heavily because I'm a programmer and no programmer would
-ever tell someone they should sprinkle repetitive bullshit all over their HTML. Resist the
-authority. Use `@apply`.
-
-Finally, you can configure other processes to run with [Ozai](https://lcthw.dev/go/ozai) in the
-`.ozai.json` file. Why so many different formats? I'm still learning which is best to use and while
-I like TOML I think I may go with `.json` files to reduce dependencies. You tell me though.
-
-## Ozai Autorestarts
-
-Ozai is a simpler tool that doesn't do auto restart. Instead it will create a little webserver at
-`http://127.0.0.1:9999` with endpoints you can hit with curl to force a restart. Look in the
-.ozai.json file to see what's configured. This makes it easy to simple add a `curl` call in your
-build process and kick everything over. Look in the `Makefile` for how I do this.
-
-This works better than the way `Air` does it for a few reasons:
-
-1. You can run your build like normal in your own IDE or build tool and see your errors directly.
- When you use other autobuild tools they have the errors so you can't access them.
-2. You get an immediate restart after your build succeeds, rather than waiting for an external tool
- to detect you made changes, run the build, and then restart your browser.
-3. You avoid the "auto reload deadlock" where you run a build, the server is shutdown, but you hit
- refresh too soon, so now your browser waits for an error, then you have to refresh again. Way
-easier to just not use any of that.
-4. No more silent failures hiding inside your terminal that you can't see. Just manually restart
- it.
-
-For more, checkout the [Ozai Project](https://lcthw.dev/go/ozai").
-
-## Tour of Directories
-
-The directories are organized in a way that separates "App Stuff" from "Content Stuff".
-
-* `admin` App The code for the built-in database admin tool.
-* `api` App Where you put your JSON API and `views` handlers.
-* `common`App Common helper functions.
-* `config` App The configuration for your site, which is `config.toml` loaded into Go structs.
-* `data` App Your database stuff. You put your SQL models in here, and then add them to the bottom `Map` to
-make them magically show up in the `/admin/table/` tool.
-* `migrations` App When you use [goose](https://github.com/pressly/goose) it puts the migrations in here.
-* `tests` App Your tests go in here. I use [chromedp](https://github.com/chromedp/chromedp) but the `chromedp`
-API is too low level (and weird) to be useful so look in `tests/tools.go` for what I use.
-* `tools` App I put my little automation tools here. I'll be adding an `admin` tool that will let you admin the
-database from the CLI, plus probably a tool to run all the things and manage them.
-* `views` App This is where you put the _App_ contents, not the static content. See the _Dev Workflow_ section
-on how to work to make dev faster but also use static files in production when things are working.
-
-These directories then control you _static_ content, but are also used by the _App_ content. For
-example, `static/` contains the `static/input_style.css` which is turned into `static/style.css`.
-The `static/style.css` is used by everything to--you guessed it--style your whole site.
-
-* `pages` Static This is the static content you want generated by `ssgod`. See _Dev Workflow_ for how I
- use this.
-* `static` Static This is static content like `.js`, images, and `.css` files that are only _copied_ over
- by `ssgod`.
-
-The following directories are considered "build junk" and you _should not_ commit them to your git.
-
-* `public` Junk This is generated by `ssgod` and then served by your `webapp.exe`. Don't
- edit anything in here, and instead edit in `pages` or `static`.
-* `tmp` Junk This is made during the build/testing process.
-
-## Dev Workflow
-
-> __Warning__ Parts of this are not optimal. I have to work on how to make this easier, parmarily
-> how to run all the things with one command and manage them.
-
-The first thing I do is run each of these commands in different terminals, either with tmux or just
-using my fingers to run multiple tabs in PowerShell:
-
-```shell
-make dev
-```
-
-### Working on Pages
-
-Once those are running in different terminals I mostly have everything I need to work in my editor
-of choice and have things autobuild. I then put my content into `pages/` and manually reload after
-`ssgod`/`tailwind` autoruns.
-
-### Working on Views
-
-Another way is to put your content into views, and then add this one line
-of code to your `api/handlers.go`. For example, if I want to create `/mypage/` I do this:
-
-```go
-# make the file with an editor on Windows
-echo "Test" > views/mypage.html
-
-# edit api/handlers.go
-app.Get("/mypage/", Page("mypage"))
-```
-
-> __Warning__ On windows the above `echo` command creates garbage in the output because Windows is
-> weird. Just make the file with a text editor.
-
-## Using the Tailwind Starter
-
-I've included [tailwind](https://tailwindcss.com/) in this setup, since it's decent at getting
-things working quickly right in the HTML. The only thing I've fully rejected is the idea that
-"@apply is bad m'kay." No, `@apply` makes it so I don't rage at all the idiots who think 92
-repeated CSS classes on every `div` is "good practice."
-
-> __NOTE__ You can view a sample page with all of these in [/examples/sample.html](/examples/sample.html) and you can look at the other files in `examples` to get an idea of what you can do. Some of these just use tailwind, others use my starter kit.
-
-In my setup there's a `static/input_style.css` that is used to generate a `static/style.css` and it
-comes preconfigured with many of the things you'll need to get a page up and running. In theory
-you can prototype a first page with just HTML and a bit of tailwind as needed. It also uses many of
-the stock HTML tags like `aside` and `mark` so you can keep things nice.
-
-I've added 4 additional layout tags for you to use:
-
-* `grid` -- For stuff in a grid. Add `class="grid-cols-2"` from tailwind to change its layout.
-* `block` -- A simple pre-configured vertical flex box. Put it around vertical stuff.
-* `bar` -- A simple pre-configured horizontal flex box. Pit it around horizontal stuff.
-* `stack` -- A simple way to stack stuff on top of other stuff. You know, like in every single 2d compositional system since the 1500s.
-* `shape` -- A simple 2d box to use for placeholders where you'll put images. It's a lot quicker to drop a little `shape` than to go find an image. I think it also makes it easier to focus on the design.
-
-### Is that "Accessible"?! What about SEO?!
-
-Yes, I actually tested all of this with [NV Access](https://www.nvaccess.org/) and the only thing NV
-Access seems to have a problem with is the `pre` and `code` tags. That's because apparently those
-are deemed as "images" so they get ignored...which is about the dumbest fucking shit I've ever
-heard. I'm working on a way to fix that.
-
-Anyone telling you that my `grid`, `bar`, `block`, or `stack` tags impact accessibility most likely
-has never ran NV Access one time so they're just wrong. Additionally, it's better to have these be
-_ignored_ by screen readers because they're just 2D layout junk, so they're kind of irrelevant to a
-linear 1D screen reader.
-
-Finally, SEO isn't impacted because Google has literally said HTML quality doesn't matter for SEO
-ranking. Think about it, if Google went around dictating exact HTML they would get in so much
-Monopoly trouble. Instead they tend to focus more on content and organization, and I believe the
-official quote is, "Making everyone use the same HTML would make the web boring."
-
-However, take this with a grain of salt because the same Google people who've said this also said
-that backlinks didn't impact performance and many SEO experts say this is totally not true. Do your
-own testing, and if it impact your SEO than just change them after you get your initial design up.
-
-## Conclusion
-
-Hopefully that gets you started in this project. I'll be improving the usability of this as I use it
-myself, but if you have suggestions please email me at help@learncodethehardway.com.
+In the `pages` directory you can write your pages using HTML or markdown. Just end the file with
+`.md` like this one and it'll be rendered with Markdown.
diff --git a/tools/cmd/fgen/main.go b/tools/cmd/fgen/main.go
index 0d85cb2..483f603 100644
--- a/tools/cmd/fgen/main.go
+++ b/tools/cmd/fgen/main.go
@@ -19,6 +19,7 @@ type Config struct {
}
func WriteTemplate(config Config, from string, to string) error {
+ fmt.Println("CREATE", to)
source, err := templates.ReadFile(from)
if err != nil { return err }
diff --git a/tools/cmd/qmgr/main.go b/tools/cmd/qmgr/main.go
index ef205cb..d849815 100644
--- a/tools/cmd/qmgr/main.go
+++ b/tools/cmd/qmgr/main.go
@@ -4,6 +4,7 @@ import (
"MY/webapp/config"
email "MY/webapp/common/email"
"context"
+ "fmt"
)
func main() {
@@ -15,9 +16,11 @@ func main() {
To: "tina.recip@example.com",
From: "toni.sender@example.com",
Subject: "This is my first mail.",
- Template: "signup",
+ Template: "signup.md",
}
+ fmt.Println("sending", msg)
+
sender := email.NewSender(ctx)
defer sender.Close()