updated README
This commit is contained in:
parent
f09320161b
commit
1692d4eb83
6 changed files with 137 additions and 58 deletions
48
README.md
48
README.md
|
@ -1,6 +1,21 @@
|
||||||
# GOTHKIT
|
# GOTHKIT
|
||||||
|
Create interactive applications with Golang, HTMX, and Templ
|
||||||
|
|
||||||
> NOT READY (YET)
|
|
||||||
|
## Table of content
|
||||||
|
- [GOTHKIT](#gothkit)
|
||||||
|
- [Table of content](#table-of-content)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Getting started](#getting-started)
|
||||||
|
- [Development server](#development-server)
|
||||||
|
- [Hot reloading the browser](#hot-reloading-the-browser)
|
||||||
|
- [Migrations](#migrations)
|
||||||
|
- [Create a new migration](#create-a-new-migration)
|
||||||
|
- [Migrate the database](#migrate-the-database)
|
||||||
|
- [Reset the database](#reset-the-database)
|
||||||
|
- [Seeds](#seeds)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [Testing handlers](#testing-handlers)
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
```
|
```
|
||||||
|
@ -12,42 +27,49 @@ After installation you can create a new project by running:
|
||||||
gothkit [myprojectname]
|
gothkit [myprojectname]
|
||||||
```
|
```
|
||||||
|
|
||||||
Navigate to your project and start the development server:
|
You can now navigate to your project:
|
||||||
```
|
```
|
||||||
cd [myprojectname]
|
cd [myprojectname]
|
||||||
|
|
||||||
make dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
## Hot reloading the browser
|
## Development server
|
||||||
Hot reloading is configured by default, the only thing left to do is watching for changes to your assets by running the following command:
|
Run the development server with the following command:
|
||||||
```
|
```
|
||||||
make assets
|
make dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Migrations
|
## Hot reloading the browser
|
||||||
### Create a new migration
|
Hot reloading is configured by default when running your application in development.
|
||||||
|
|
||||||
|
> NOTE: on windows you might need to run `make assets` in another terminal for god knows why.
|
||||||
|
|
||||||
|
# Migrations
|
||||||
|
## Create a new migration
|
||||||
```
|
```
|
||||||
make db-mig add_user_table
|
make db-mig add_user_table
|
||||||
```
|
```
|
||||||
|
|
||||||
### Migrate the database
|
Will create a new migration SQL file inside `app/db/migrations`
|
||||||
|
|
||||||
|
## Migrate the database
|
||||||
```
|
```
|
||||||
make db-up
|
make db-up
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reset the database
|
## Reset the database
|
||||||
```
|
```
|
||||||
make db-reset
|
make db-reset
|
||||||
```
|
```
|
||||||
|
|
||||||
### Seed the database
|
## Seeds
|
||||||
```
|
```
|
||||||
make db-seed
|
make db-seed
|
||||||
```
|
```
|
||||||
|
This command will run the seeds file located at `cmd/scripts/seed/main.go`
|
||||||
|
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
## Testing handlers
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package components
|
||||||
templ Navigation() {
|
templ Navigation() {
|
||||||
<nav class="border-b py-3">
|
<nav class="border-b py-3">
|
||||||
<div class="container mx-auto flex justify-between">
|
<div class="container mx-auto flex justify-between">
|
||||||
<div class="text-lg text-red-500">
|
<div class="text-lg text-foreground">
|
||||||
<a href="/" class="font-semibold uppercase">Gothkit <span class="text-sm">v0.1</span></a>
|
<a href="/" class="font-semibold uppercase">Gothkit <span class="text-sm">v0.1</span></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-4 items-center">
|
<div class="flex gap-4 items-center">
|
||||||
|
|
|
@ -7,8 +7,10 @@ import (
|
||||||
templ Index() {
|
templ Index() {
|
||||||
@layouts.App() {
|
@layouts.App() {
|
||||||
<div class="mt-20 text-center flex flex-col gap-10">
|
<div class="mt-20 text-center flex flex-col gap-10">
|
||||||
<h1 class="max-w-xl mx-auto tracking-wider text-3xl lg:text-6xl font-bold">A single binary to change the world</h1>
|
<h1 class="max-w-xl mx-auto text-3xl lg:text-6xl font-bold">A single binary to change the world</h1>
|
||||||
<a class="text-xl underline text-blue-500" target="_blank" href="https://github.com/anthdm/gothkit">Get started by reading the documentation</a>
|
<div class="flex justify-center">
|
||||||
|
<a href="https://github.com/anthdm/gothkit" target="_blank" class="bg-primary w-fit rounded-md px-4 py-2 text-primary-foreground">Getting started</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ html,
|
||||||
-o-tab-size: 4;
|
-o-tab-size: 4;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
/* 3 */
|
/* 3 */
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
/* 4 */
|
/* 4 */
|
||||||
font-feature-settings: normal;
|
font-feature-settings: normal;
|
||||||
/* 5 */
|
/* 5 */
|
||||||
|
@ -628,6 +628,10 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
.h-screen {
|
.h-screen {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
@ -636,6 +640,11 @@ body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-fit {
|
||||||
|
width: -moz-fit-content;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.max-w-7xl {
|
.max-w-7xl {
|
||||||
max-width: 80rem;
|
max-width: 80rem;
|
||||||
}
|
}
|
||||||
|
@ -644,10 +653,18 @@ body {
|
||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-w-5xl {
|
||||||
|
max-width: 64rem;
|
||||||
|
}
|
||||||
|
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-cols-2 {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
@ -672,12 +689,12 @@ body {
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gap-8 {
|
.rounded-md {
|
||||||
gap: 2rem;
|
border-radius: calc(var(--radius) - 2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rounded-full {
|
.rounded-sm {
|
||||||
border-radius: 9999px;
|
border-radius: calc(var(--radius) - 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.border {
|
.border {
|
||||||
|
@ -688,17 +705,13 @@ body {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-black {
|
.bg-primary {
|
||||||
--tw-border-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
background-color: hsl(var(--primary) / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-1 {
|
.p-2 {
|
||||||
padding: 0.25rem;
|
padding: 0.5rem;
|
||||||
}
|
|
||||||
|
|
||||||
.p-1\.5 {
|
|
||||||
padding: 0.375rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.py-3 {
|
.py-3 {
|
||||||
|
@ -706,6 +719,31 @@ body {
|
||||||
padding-bottom: 0.75rem;
|
padding-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.px-1 {
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
padding-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.py-1 {
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.py-2 {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.px-3 {
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.px-4 {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.text-center {
|
.text-center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -747,12 +785,12 @@ body {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uppercase {
|
.font-black {
|
||||||
text-transform: uppercase;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-wide {
|
.uppercase {
|
||||||
letter-spacing: 0.025em;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-wider {
|
.tracking-wider {
|
||||||
|
@ -774,6 +812,26 @@ body {
|
||||||
color: rgb(239 68 68 / var(--tw-text-opacity));
|
color: rgb(239 68 68 / var(--tw-text-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-white {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-foreground {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: hsl(var(--foreground) / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: hsl(var(--primary) / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary-foreground {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: hsl(var(--primary-foreground) / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.underline {
|
.underline {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
|
@ -784,10 +842,3 @@ body {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.dark\:border-white {
|
|
||||||
--tw-border-opacity: 1;
|
|
||||||
border-color: rgb(255 255 255 / var(--tw-border-opacity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -72,19 +72,14 @@ var ContainsUpper = RuleSet{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ContainsNumeric = RuleSet{
|
var ContainsDigit = RuleSet{
|
||||||
Name: "containsNumeric",
|
Name: "containsDigit",
|
||||||
ValidateFunc: func(rule RuleSet) bool {
|
ValidateFunc: func(rule RuleSet) bool {
|
||||||
str, ok := rule.FieldValue.(string)
|
str, ok := rule.FieldValue.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, ch := range str {
|
return hasDigit(str)
|
||||||
if isSpecial(ch) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
MessageFunc: func(set RuleSet) string {
|
MessageFunc: func(set RuleSet) string {
|
||||||
return "must contain at least 1 numeric character"
|
return "must contain at least 1 numeric character"
|
||||||
|
@ -98,12 +93,7 @@ var ContainsSpecial = RuleSet{
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, ch := range str {
|
return hasSpecialChar(str)
|
||||||
if isSpecial(ch) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
MessageFunc: func(set RuleSet) string {
|
MessageFunc: func(set RuleSet) string {
|
||||||
return "must contain at least 1 special character"
|
return "must contain at least 1 special character"
|
||||||
|
@ -297,6 +287,20 @@ func Min(n int) RuleSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSpecial(ch rune) bool {
|
func hasDigit(s string) bool {
|
||||||
return true
|
for _, char := range s {
|
||||||
|
if unicode.IsDigit(char) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasSpecialChar(s string) bool {
|
||||||
|
for _, char := range s {
|
||||||
|
if !unicode.IsLetter(char) && !unicode.IsDigit(char) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ var testSchema = Schema{
|
||||||
"password": Rules(
|
"password": Rules(
|
||||||
ContainsSpecial,
|
ContainsSpecial,
|
||||||
ContainsUpper,
|
ContainsUpper,
|
||||||
ContainsNumeric,
|
ContainsDigit,
|
||||||
Min(7),
|
Min(7),
|
||||||
Max(50),
|
Max(50),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue