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
|
||||
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
|
||||
```
|
||||
|
@ -12,42 +27,49 @@ After installation you can create a new project by running:
|
|||
gothkit [myprojectname]
|
||||
```
|
||||
|
||||
Navigate to your project and start the development server:
|
||||
You can now navigate to your project:
|
||||
```
|
||||
cd [myprojectname]
|
||||
|
||||
make dev
|
||||
```
|
||||
|
||||
# Getting started
|
||||
## Hot reloading the browser
|
||||
Hot reloading is configured by default, the only thing left to do is watching for changes to your assets by running the following command:
|
||||
## Development server
|
||||
Run the development server with the following command:
|
||||
```
|
||||
make assets
|
||||
make dev
|
||||
```
|
||||
|
||||
## Migrations
|
||||
### Create a new migration
|
||||
## Hot reloading the browser
|
||||
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
|
||||
```
|
||||
|
||||
### Migrate the database
|
||||
Will create a new migration SQL file inside `app/db/migrations`
|
||||
|
||||
## Migrate the database
|
||||
```
|
||||
make db-up
|
||||
```
|
||||
|
||||
### Reset the database
|
||||
## Reset the database
|
||||
```
|
||||
make db-reset
|
||||
```
|
||||
|
||||
### Seed the database
|
||||
## Seeds
|
||||
```
|
||||
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() {
|
||||
<nav class="border-b py-3">
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex gap-4 items-center">
|
||||
|
|
|
@ -7,8 +7,10 @@ import (
|
|||
templ Index() {
|
||||
@layouts.App() {
|
||||
<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>
|
||||
<a class="text-xl underline text-blue-500" target="_blank" href="https://github.com/anthdm/gothkit">Get started by reading the documentation</a>
|
||||
<h1 class="max-w-xl mx-auto text-3xl lg:text-6xl font-bold">A single binary to change the world</h1>
|
||||
<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>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ html,
|
|||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 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 */
|
||||
font-feature-settings: normal;
|
||||
/* 5 */
|
||||
|
@ -628,6 +628,10 @@ body {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
|
@ -636,6 +640,11 @@ body {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.w-fit {
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.max-w-7xl {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
@ -644,10 +653,18 @@ body {
|
|||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.max-w-5xl {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -672,12 +689,12 @@ body {
|
|||
gap: 1rem;
|
||||
}
|
||||
|
||||
.gap-8 {
|
||||
gap: 2rem;
|
||||
.rounded-md {
|
||||
border-radius: calc(var(--radius) - 2px);
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 9999px;
|
||||
.rounded-sm {
|
||||
border-radius: calc(var(--radius) - 4px);
|
||||
}
|
||||
|
||||
.border {
|
||||
|
@ -688,17 +705,13 @@ body {
|
|||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.border-black {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: hsl(var(--primary) / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.p-1\.5 {
|
||||
padding: 0.375rem;
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
|
@ -706,6 +719,31 @@ body {
|
|||
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-align: center;
|
||||
}
|
||||
|
@ -747,12 +785,12 @@ body {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
.font-black {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.tracking-wide {
|
||||
letter-spacing: 0.025em;
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tracking-wider {
|
||||
|
@ -774,6 +812,26 @@ body {
|
|||
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 {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
@ -784,10 +842,3 @@ body {
|
|||
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{
|
||||
Name: "containsNumeric",
|
||||
var ContainsDigit = RuleSet{
|
||||
Name: "containsDigit",
|
||||
ValidateFunc: func(rule RuleSet) bool {
|
||||
str, ok := rule.FieldValue.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, ch := range str {
|
||||
if isSpecial(ch) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return hasDigit(str)
|
||||
},
|
||||
MessageFunc: func(set RuleSet) string {
|
||||
return "must contain at least 1 numeric character"
|
||||
|
@ -98,12 +93,7 @@ var ContainsSpecial = RuleSet{
|
|||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, ch := range str {
|
||||
if isSpecial(ch) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return hasSpecialChar(str)
|
||||
},
|
||||
MessageFunc: func(set RuleSet) string {
|
||||
return "must contain at least 1 special character"
|
||||
|
@ -297,6 +287,20 @@ func Min(n int) RuleSet {
|
|||
}
|
||||
}
|
||||
|
||||
func isSpecial(ch rune) bool {
|
||||
return true
|
||||
func hasDigit(s string) bool {
|
||||
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(
|
||||
ContainsSpecial,
|
||||
ContainsUpper,
|
||||
ContainsNumeric,
|
||||
ContainsDigit,
|
||||
Min(7),
|
||||
Max(50),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue