Compare commits
151 commits
c72257132c
...
dfefe65ec0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfefe65ec0 | ||
|
|
04f37f87e2 | ||
|
|
db9fe90163 | ||
|
|
acb12c393a | ||
|
|
9d75c2a665 | ||
|
|
f154d42e67 | ||
|
|
37104788e7 | ||
|
|
75c9c5be34 | ||
|
|
f634285909 | ||
|
|
1641ac51a6 | ||
|
|
2eefb46e08 | ||
|
|
fd6218c0ad | ||
|
|
79a589da65 | ||
|
|
2a546dfe3d | ||
|
|
885a6ad7bf | ||
|
|
4c4a503cdc | ||
|
|
0d6e26ab39 | ||
|
|
e85ca0a93f | ||
|
|
8d6f0b1261 | ||
|
|
7dcf6bc039 | ||
|
|
d91774313d | ||
|
|
efab433d5c | ||
|
|
9441014517 | ||
|
|
fbb3fdc70d | ||
|
|
a7c6994062 | ||
|
|
62d46c475c | ||
|
|
5cad4cbd33 | ||
|
|
633c60cdf5 | ||
|
|
ca1de66033 | ||
|
|
b808500a23 | ||
|
|
b2ad889f56 | ||
|
|
a7006b3997 | ||
|
|
d2d8c02a2f | ||
|
|
02a58946f1 | ||
|
|
d736551869 | ||
|
|
e3217fc852 | ||
|
|
c244389971 | ||
|
|
a9319559e1 | ||
|
|
f54d9f8b37 | ||
|
|
062d1f70be | ||
|
|
73098499dd | ||
|
|
6da0fcb7be | ||
|
|
62c53a6b4d | ||
|
|
11def45666 | ||
|
|
ca22f54c89 | ||
|
|
6730b6a319 | ||
|
|
71f7de8771 | ||
|
|
5531e0bec2 | ||
|
|
6a7070a729 | ||
|
|
9acf73a4d9 | ||
|
|
5ebd42d8f9 | ||
|
|
7d85ff0b08 | ||
|
|
afa8b5d2cc | ||
|
|
28abc92e74 | ||
|
|
400b9b36ba | ||
|
|
2c635b5c75 | ||
|
|
30389de16f | ||
|
|
0e204428b6 | ||
|
|
0783709b3c | ||
|
|
a122851717 | ||
|
|
114ed8444c | ||
|
|
f6a96e2025 | ||
|
|
15739cc82e | ||
|
|
e8952e964c | ||
|
|
b1c47426d9 | ||
|
|
97cd9d0fee | ||
|
|
c2b6928fb4 | ||
|
|
5af18e2473 | ||
|
|
81c65fcc30 | ||
|
|
3df20c01a7 | ||
|
|
0879aaf21d | ||
|
|
ba96445704 | ||
|
|
42c7957bf4 | ||
|
|
44da53d99f | ||
|
|
6a3f4b5ff2 | ||
|
|
68b850c87b | ||
|
|
4867b9bab8 | ||
|
|
7eae6309fd | ||
|
|
3026ec218e | ||
|
|
f688bff1d2 | ||
|
|
575bf805e2 | ||
|
|
1f42ce7db8 | ||
|
|
d875747e31 | ||
|
|
fec8698526 | ||
|
|
6d3dfe2531 | ||
|
|
842ddbdc9d | ||
|
|
4f776b76b3 | ||
|
|
96799be6ec | ||
|
|
859925bc45 | ||
|
|
911793ec61 | ||
|
|
19315b894e | ||
|
|
5b73526436 | ||
|
|
baa65ebbfa | ||
|
|
4527d3d2e5 | ||
|
|
2ac7604b43 | ||
|
|
713d3818bb | ||
|
|
26e4d4d16a | ||
|
|
0f800ec3e1 | ||
|
|
c2f1df1a83 | ||
|
|
8c5582f720 | ||
|
|
72ce41c828 | ||
|
|
1018d82d13 | ||
|
|
434d7b44b0 | ||
|
|
d85159c4f0 | ||
|
|
152e2e1037 | ||
|
|
44ab335cf5 | ||
|
|
936e6c9b18 | ||
|
|
55d51c70b8 | ||
|
|
9f6fe5f0b7 | ||
|
|
7f1e057ea2 | ||
|
|
12a177a215 | ||
|
|
c52544caf0 | ||
|
|
fb55c3453b | ||
|
|
0cc95d4c64 | ||
|
|
9f1ed2e5f5 | ||
|
|
8d2a6d9b7a | ||
|
|
aab3c04473 | ||
|
|
8fa6ebb8cf | ||
|
|
acebcf0ba0 | ||
|
|
cd1887124e | ||
|
|
0ca3c2b701 | ||
|
|
5c51b72757 | ||
|
|
8b1bc9b99e | ||
|
|
089c4d7ee1 | ||
|
|
51e44a57a1 | ||
|
|
944a4d941a | ||
|
|
c6a50b3bbf | ||
|
|
83cdbc4395 | ||
|
|
726556e973 | ||
|
|
156e578dd0 | ||
|
|
c43f62a570 | ||
|
|
a82ed9c6d0 | ||
|
|
6546418052 | ||
|
|
7a1a01d43e | ||
|
|
107f2e3262 | ||
|
|
cd4cc1693c | ||
|
|
cb43e08183 | ||
|
|
b269e7d264 | ||
|
|
e0a65ca007 | ||
|
|
09c6df7f52 | ||
|
|
10c0a23c0a | ||
|
|
a6e99058f4 | ||
|
|
c31f30ba5c | ||
|
|
6ec7118c3d | ||
|
|
eda79b6982 | ||
|
|
c391be9e4d | ||
|
|
2ece37eb9c | ||
|
|
328c1a3367 | ||
|
|
182ddc9f52 | ||
|
|
17185bee70 | ||
|
|
a6e131119a |
208 changed files with 11621 additions and 8207 deletions
52
.air.toml
Normal file
52
.air.toml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "make build"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "uploads", "dbs", "public"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "css"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = true
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
silent = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
31
.github/workflows/test.yml
vendored
Normal file
31
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.23
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1 +1,6 @@
|
|||
.idea
|
||||
dbs
|
||||
uploads
|
||||
tmp
|
||||
tailwindcss
|
||||
daisyui*
|
||||
104
Makefile
104
Makefile
|
|
@ -1,52 +1,82 @@
|
|||
# Connect to the primary database
|
||||
.PHONY: db
|
||||
db:
|
||||
psql postgresql://admin:admin@localhost:5432/app
|
||||
# Get the OS name in lowercase (linux, darwin)
|
||||
OS_SYSNAME := $(shell uname -s | tr A-Z a-z)
|
||||
# Get the machine architecture (x86_64, arm64)
|
||||
OS_MACHINE := $(shell uname -m)
|
||||
|
||||
# Connect to the test database
|
||||
.PHONY: db-test
|
||||
db-test:
|
||||
psql postgresql://admin:admin@localhost:5432/app_test
|
||||
# If mac OS, use `macos-arm64` or `macos-x64`
|
||||
ifeq ($(OS_SYSNAME),darwin)
|
||||
OS_SYSNAME = macos
|
||||
ifneq ($(OS_MACHINE),arm64)
|
||||
OS_MACHINE = x64
|
||||
endif
|
||||
endif
|
||||
|
||||
# Connect to the cache
|
||||
.PHONY: cache
|
||||
cache:
|
||||
redis-cli
|
||||
# If Linux, use `linux-x64`
|
||||
ifeq ($(OS_SYSNAME),linux)
|
||||
OS_MACHINE = x64
|
||||
endif
|
||||
|
||||
# The appropriate Tailwind package for your OS will attempt to be automatically determined.
|
||||
# If this is not working, hard-code the package you want using these options:
|
||||
# https://github.com/tailwindlabs/tailwindcss/releases/latest
|
||||
TAILWIND_PACKAGE = tailwindcss-$(OS_SYSNAME)-$(OS_MACHINE)
|
||||
|
||||
.PHONY: help
|
||||
help: ## Print make targets
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
.PHONY: install
|
||||
install: ent-install air-install tailwind-install ## Install all dependencies
|
||||
|
||||
.PHONY: tailwind-install
|
||||
tailwind-install: ## Install the Tailwind CSS CLI
|
||||
curl -sLo tailwindcss https://github.com/tailwindlabs/tailwindcss/releases/latest/download/$(TAILWIND_PACKAGE)
|
||||
chmod +x tailwindcss
|
||||
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui.js
|
||||
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui-theme.js
|
||||
|
||||
# Install Ent code-generation module
|
||||
.PHONY: ent-install
|
||||
ent-install:
|
||||
go get -d entgo.io/ent/cmd/ent
|
||||
ent-install: ## Install Ent code-generation module
|
||||
go get entgo.io/ent/cmd/ent
|
||||
|
||||
.PHONY: air-install
|
||||
air-install: ## Install air
|
||||
go install github.com/air-verse/air@latest
|
||||
|
||||
# Generate Ent code
|
||||
.PHONY: ent-gen
|
||||
ent-gen:
|
||||
ent-gen: ## Generate Ent code
|
||||
go generate ./ent
|
||||
|
||||
# Create a new Ent entity
|
||||
.PHONY: ent-new
|
||||
ent-new:
|
||||
go run entgo.io/ent/cmd/ent init $(name)
|
||||
ent-new: ## Create a new Ent entity (ie, make ent-new name=MyEntity)
|
||||
go run entgo.io/ent/cmd/ent new $(name)
|
||||
|
||||
# Start the Docker containers
|
||||
.PHONY: up
|
||||
up:
|
||||
docker-compose up -d
|
||||
sleep 3
|
||||
.PHONY: admin
|
||||
admin: ## Create a new admin user (ie, make admin email=myemail@web.com)
|
||||
go run cmd/admin/main.go --email=$(email)
|
||||
|
||||
# Rebuild Docker containers to wipe all data
|
||||
.PHONY: reset
|
||||
reset:
|
||||
docker-compose down
|
||||
make up
|
||||
|
||||
# Run the application
|
||||
.PHONY: run
|
||||
run:
|
||||
run: ## Run the application
|
||||
clear
|
||||
go run main.go
|
||||
go run cmd/web/main.go
|
||||
|
||||
.PHONY: watch
|
||||
watch: ## Run the application and watch for changes with air to automatically rebuild
|
||||
clear
|
||||
air
|
||||
|
||||
# Run all tests
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -p 1 ./...
|
||||
test: ## Run all tests
|
||||
go test ./...
|
||||
|
||||
.PHONY: check-updates
|
||||
check-updates: ## Check for direct dependency updates
|
||||
go list -u -m -f '{{if not .Indirect}}{{.}}{{end}}' all | grep "\["
|
||||
|
||||
.PHONY: css
|
||||
css: ## Build and minify Tailwind CSS
|
||||
./tailwindcss -i tailwind.css -o public/static/main.css -m
|
||||
|
||||
.PHONY: build
|
||||
build: css ## Build CSS and compile the application binary
|
||||
go build -o ./tmp/main ./cmd/web
|
||||
|
|
|
|||
63
cmd/admin/main.go
Normal file
63
cmd/admin/main.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
)
|
||||
|
||||
// main creates a new admin user with the email passed in via the flag.
|
||||
func main() {
|
||||
// Start a new container.
|
||||
c := services.NewContainer()
|
||||
defer func() {
|
||||
// Gracefully shutdown all services.
|
||||
if err := c.Shutdown(); err != nil {
|
||||
log.Default().Error("shutdown failed", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var email string
|
||||
flag.StringVar(&email, "email", "", "email address for the admin user")
|
||||
flag.Parse()
|
||||
|
||||
if len(email) == 0 {
|
||||
invalid("email is required")
|
||||
}
|
||||
|
||||
// Generate a password.
|
||||
pw, err := c.Auth.RandomToken(10)
|
||||
if err != nil {
|
||||
invalid("failed to generate a random password")
|
||||
}
|
||||
|
||||
// Create the admin user.
|
||||
err = c.ORM.User.
|
||||
Create().
|
||||
SetEmail(email).
|
||||
SetName("Admin").
|
||||
SetAdmin(true).
|
||||
SetVerified(true).
|
||||
SetPassword(pw).
|
||||
Exec(context.Background())
|
||||
|
||||
if err != nil {
|
||||
invalid(err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("-- ADMIN USER CREATED --")
|
||||
fmt.Printf("Email: %s\n", email)
|
||||
fmt.Printf("Password: %s\n", pw)
|
||||
fmt.Println("----")
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func invalid(msg string) {
|
||||
fmt.Printf("[ERROR] %s\n", msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
74
cmd/web/main.go
Normal file
74
cmd/web/main.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/mikestefanello/pagoda/pkg/handlers"
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/tasks"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Start a new container.
|
||||
c := services.NewContainer()
|
||||
defer func() {
|
||||
// Gracefully shutdown all services.
|
||||
fatal("shutdown failed", c.Shutdown())
|
||||
}()
|
||||
|
||||
// Build the router.
|
||||
if err := handlers.BuildRouter(c); err != nil {
|
||||
fatal("failed to build the router", err)
|
||||
}
|
||||
|
||||
// Register all task queues.
|
||||
tasks.Register(c)
|
||||
|
||||
// Start the task runner to execute queued tasks.
|
||||
c.Tasks.Start(context.Background())
|
||||
|
||||
// Start the server.
|
||||
go func() {
|
||||
srv := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", c.Config.HTTP.Hostname, c.Config.HTTP.Port),
|
||||
Handler: c.Web,
|
||||
ReadTimeout: c.Config.HTTP.ReadTimeout,
|
||||
WriteTimeout: c.Config.HTTP.WriteTimeout,
|
||||
IdleTimeout: c.Config.HTTP.IdleTimeout,
|
||||
}
|
||||
|
||||
if c.Config.HTTP.TLS.Enabled {
|
||||
certs, err := tls.LoadX509KeyPair(c.Config.HTTP.TLS.Certificate, c.Config.HTTP.TLS.Key)
|
||||
fatal("cannot load TLS certificate", err)
|
||||
|
||||
srv.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{certs},
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Web.StartServer(&srv); errors.Is(err, http.ErrServerClosed) {
|
||||
fatal("shutting down the server", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shut down the web server and task runner.
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
signal.Notify(quit, os.Kill)
|
||||
<-quit
|
||||
}
|
||||
|
||||
// fatal logs an error and terminates the application, if the error is not nil.
|
||||
func fatal(msg string, err error) {
|
||||
if err != nil {
|
||||
log.Default().Error(msg, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
161
config/config.go
161
config/config.go
|
|
@ -2,115 +2,146 @@ package config
|
|||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joeshaw/envdecode"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
// TemplateDir stores the name of the directory that contains templates
|
||||
TemplateDir = "templates"
|
||||
|
||||
// TemplateExt stores the extension used for the template files
|
||||
TemplateExt = ".gohtml"
|
||||
|
||||
// StaticDir stores the name of the directory that will serve static files
|
||||
StaticDir = "static"
|
||||
|
||||
// StaticPrefix stores the URL prefix used when serving static files
|
||||
StaticPrefix = "files"
|
||||
)
|
||||
|
||||
type Environment string
|
||||
type environment string
|
||||
|
||||
const (
|
||||
EnvLocal Environment = "local"
|
||||
EnvTest Environment = "test"
|
||||
EnvDevelop Environment = "dev"
|
||||
EnvStaging Environment = "staging"
|
||||
EnvQA Environment = "qa"
|
||||
EnvProduction Environment = "prod"
|
||||
// EnvLocal represents the local environment.
|
||||
EnvLocal environment = "local"
|
||||
|
||||
// EnvTest represents the test environment.
|
||||
EnvTest environment = "test"
|
||||
|
||||
// EnvDevelopment represents the development environment.
|
||||
EnvDevelopment environment = "dev"
|
||||
|
||||
// EnvStaging represents the staging environment.
|
||||
EnvStaging environment = "staging"
|
||||
|
||||
// EnvQA represents the qa environment.
|
||||
EnvQA environment = "qa"
|
||||
|
||||
// EnvProduction represents the production environment.
|
||||
EnvProduction environment = "prod"
|
||||
)
|
||||
|
||||
// SwitchEnvironment sets the environment variable used to dictate which environment the application is
|
||||
// currently running in.
|
||||
// This must be called prior to loading the configuration in order for it to take effect.
|
||||
func SwitchEnvironment(env Environment) {
|
||||
if err := os.Setenv("APP_ENVIRONMENT", string(env)); err != nil {
|
||||
func SwitchEnvironment(env environment) {
|
||||
if err := os.Setenv("PAGODA_APP_ENVIRONMENT", string(env)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// Config stores complete configuration
|
||||
// Config stores complete configuration.
|
||||
Config struct {
|
||||
HTTP HTTPConfig
|
||||
App AppConfig
|
||||
Cache CacheConfig
|
||||
Database DatabaseConfig
|
||||
Files FilesConfig
|
||||
Tasks TasksConfig
|
||||
Mail MailConfig
|
||||
}
|
||||
|
||||
// HTTPConfig stores HTTP configuration
|
||||
// HTTPConfig stores HTTP configuration.
|
||||
HTTPConfig struct {
|
||||
Hostname string `env:"HTTP_HOSTNAME"`
|
||||
Port uint16 `env:"HTTP_PORT,default=8000"`
|
||||
ReadTimeout time.Duration `env:"HTTP_READ_TIMEOUT,default=5s"`
|
||||
WriteTimeout time.Duration `env:"HTTP_WRITE_TIMEOUT,default=10s"`
|
||||
IdleTimeout time.Duration `env:"HTTP_IDLE_TIMEOUT,default=2m"`
|
||||
TLS struct {
|
||||
Enabled bool `env:"HTTP_TLS_ENABLED,default=false"`
|
||||
Certificate string `env:"HTTP_TLS_CERTIFICATE"`
|
||||
Key string `env:"HTTP_TLS_KEY"`
|
||||
Hostname string
|
||||
Port uint16
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
ShutdownTimeout time.Duration
|
||||
TLS struct {
|
||||
Enabled bool
|
||||
Certificate string
|
||||
Key string
|
||||
}
|
||||
}
|
||||
|
||||
// AppConfig stores application configuration
|
||||
// AppConfig stores application configuration.
|
||||
AppConfig struct {
|
||||
Name string `env:"APP_NAME,default=Goweb"`
|
||||
Environment Environment `env:"APP_ENVIRONMENT,default=local"`
|
||||
EncryptionKey string `env:"APP_ENCRYPTION_KEY,default=?E(G+KbPeShVmYq3t6w9z$C&F)J@McQf"`
|
||||
Timeout time.Duration `env:"APP_TIMEOUT,default=20s"`
|
||||
Name string
|
||||
Host string
|
||||
Environment environment
|
||||
EncryptionKey string
|
||||
Timeout time.Duration
|
||||
PasswordToken struct {
|
||||
Expiration time.Duration `env:"APP_PASSWORD_TOKEN_EXPIRATION,default=60m"`
|
||||
Length int `env:"APP_PASSWORD_TOKEN_LENGTH,default=64"`
|
||||
Expiration time.Duration
|
||||
Length int
|
||||
}
|
||||
EmailVerificationTokenExpiration time.Duration
|
||||
}
|
||||
|
||||
// CacheConfig stores the cache configuration
|
||||
// CacheConfig stores the cache configuration.
|
||||
CacheConfig struct {
|
||||
Hostname string `env:"CACHE_HOSTNAME,default=localhost"`
|
||||
Port uint16 `env:"CACHE_PORT,default=6379"`
|
||||
Password string `env:"CACHE_PASSWORD"`
|
||||
Capacity int
|
||||
Expiration struct {
|
||||
StaticFile time.Duration `env:"CACHE_EXPIRATION_STATIC_FILE,default=4380h"`
|
||||
Page time.Duration `env:"CACHE_EXPIRATION_PAGE,default=24h"`
|
||||
PublicFile time.Duration
|
||||
}
|
||||
}
|
||||
|
||||
// DatabaseConfig stores the database configuration
|
||||
// DatabaseConfig stores the database configuration.
|
||||
DatabaseConfig struct {
|
||||
Hostname string `env:"DB_HOSTNAME,default=localhost"`
|
||||
Port uint16 `env:"DB_PORT,default=5432"`
|
||||
User string `env:"DB_USER,default=admin"`
|
||||
Password string `env:"DB_PASSWORD,default=admin"`
|
||||
Database string `env:"DB_NAME,default=app"`
|
||||
TestDatabase string `env:"DB_NAME_TEST,default=app_test"`
|
||||
Driver string
|
||||
Connection string
|
||||
TestConnection string
|
||||
}
|
||||
|
||||
// MailConfig stores the mail configuration
|
||||
// FilesConfig stores the file system configuration.
|
||||
FilesConfig struct {
|
||||
Directory string
|
||||
}
|
||||
|
||||
// TasksConfig stores the tasks configuration.
|
||||
TasksConfig struct {
|
||||
Goroutines int
|
||||
ReleaseAfter time.Duration
|
||||
CleanupInterval time.Duration
|
||||
ShutdownTimeout time.Duration
|
||||
}
|
||||
|
||||
// MailConfig stores the mail configuration.
|
||||
MailConfig struct {
|
||||
Hostname string `env:"MAIL_HOSTNAME,default=localhost"`
|
||||
Port uint16 `env:"MAIL_PORT,default=25"`
|
||||
User string `env:"MAIL_USER,default=admin"`
|
||||
Password string `env:"MAIL_PASSWORD,default=admin"`
|
||||
FromAddress string `env:"MAIL_FROM_ADDRESS,default=admin@localhost"`
|
||||
Hostname string
|
||||
Port uint16
|
||||
User string
|
||||
Password string
|
||||
FromAddress string
|
||||
}
|
||||
)
|
||||
|
||||
// GetConfig loads and returns configuration
|
||||
// GetConfig loads and returns configuration.
|
||||
func GetConfig() (Config, error) {
|
||||
var cfg Config
|
||||
err := envdecode.StrictDecode(&cfg)
|
||||
return cfg, err
|
||||
var c Config
|
||||
|
||||
// Load the config file.
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("config")
|
||||
viper.AddConfigPath("../config")
|
||||
viper.AddConfigPath("../../config")
|
||||
|
||||
// Load env variables.
|
||||
viper.SetEnvPrefix("pagoda")
|
||||
viper.AutomaticEnv()
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
if err := viper.Unmarshal(&c); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
|
|
|||
54
config/config.yaml
Normal file
54
config/config.yaml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
http:
|
||||
hostname: ""
|
||||
port: 8000
|
||||
readTimeout: "5s"
|
||||
writeTimeout: "10s"
|
||||
idleTimeout: "2m"
|
||||
shutdownTimeout: "10s"
|
||||
tls:
|
||||
enabled: false
|
||||
certificate: ""
|
||||
key: ""
|
||||
|
||||
app:
|
||||
name: "Pagoda"
|
||||
# We manually set this rather than using the HTTP settings in order to build absolute URLs for users
|
||||
# since it's likely your app's HTTP settings are not identical to what is exposed by your server.
|
||||
host: "http://localhost:8000"
|
||||
environment: "local"
|
||||
# Change this on any live environments.
|
||||
encryptionKey: "?E(G+KbPeShVmYq3t6w9z$C&F)J@McQf"
|
||||
timeout: "20s"
|
||||
passwordToken:
|
||||
expiration: "60m"
|
||||
length: 64
|
||||
emailVerificationTokenExpiration: "12h"
|
||||
|
||||
cache:
|
||||
capacity: 100000
|
||||
expiration:
|
||||
publicFile: "4380h"
|
||||
|
||||
database:
|
||||
driver: "sqlite3"
|
||||
connection: "dbs/main.db?_journal=WAL&_timeout=5000&_fk=true"
|
||||
# $RAND will be automatically replaced with a random value.
|
||||
# memdb is more robust for an in-memory database rather than :memory: because the latter has the potential
|
||||
# retain data even after you close and re-open the connection.
|
||||
testConnection: "file:/$RAND?vfs=memdb&_timeout=1000&_fk=true"
|
||||
|
||||
files:
|
||||
directory: "uploads"
|
||||
|
||||
tasks:
|
||||
goroutines: 1
|
||||
releaseAfter: "15m"
|
||||
cleanupInterval: "1h"
|
||||
shutdownTimeout: "10s"
|
||||
|
||||
mail:
|
||||
hostname: "localhost"
|
||||
port: 25
|
||||
user: "admin"
|
||||
password: "admin"
|
||||
fromAddress: "admin@localhost"
|
||||
|
|
@ -11,7 +11,7 @@ func TestGetConfig(t *testing.T) {
|
|||
_, err := GetConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
var env Environment
|
||||
var env environment
|
||||
env = "abc"
|
||||
SwitchEnvironment(env)
|
||||
cfg, err := GetConfig()
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
package context
|
||||
|
||||
const (
|
||||
// AuthenticatedUserKey is the key value used to store the authenticated user in context
|
||||
AuthenticatedUserKey = "auth_user"
|
||||
|
||||
// UserKey is the key value used to store a user in context
|
||||
UserKey = "user"
|
||||
|
||||
// FormKey is the key value used to store a form in context
|
||||
FormKey = "form"
|
||||
|
||||
// PasswordTokenKey is the key value used to store a password token in context
|
||||
PasswordTokenKey = "password_token"
|
||||
)
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"goweb/middleware"
|
||||
"goweb/services"
|
||||
|
||||
"github.com/eko/gocache/v2/marshaler"
|
||||
|
||||
"github.com/eko/gocache/v2/store"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Controller provides base functionality and dependencies to routes.
|
||||
// The proposed pattern is to embed a Controller in each individual route struct and to use
|
||||
// the router to inject the container so your routes have access to the services within the container
|
||||
type Controller struct {
|
||||
// Container stores a services container which contains dependencies
|
||||
Container *services.Container
|
||||
}
|
||||
|
||||
// NewController creates a new Controller
|
||||
func NewController(c *services.Container) Controller {
|
||||
return Controller{
|
||||
Container: c,
|
||||
}
|
||||
}
|
||||
|
||||
// RenderPage renders a Page as an HTTP response
|
||||
func (c *Controller) RenderPage(ctx echo.Context, page Page) error {
|
||||
var buf *bytes.Buffer
|
||||
var err error
|
||||
|
||||
// Page name is required
|
||||
if page.Name == "" {
|
||||
ctx.Logger().Error("page render failed due to missing name")
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Use the app name in configuration if a value was not set
|
||||
if page.AppName == "" {
|
||||
page.AppName = c.Container.Config.App.Name
|
||||
}
|
||||
|
||||
// Check if this is an HTMX non-boosted request which indicates that only partial
|
||||
// content should be rendered
|
||||
if page.HTMX.Request.Enabled && !page.HTMX.Request.Boosted {
|
||||
// Parse and execute the templates only for the content portion of the page
|
||||
// The templates used for this partial request will be:
|
||||
// 1. The base htmx template which omits the layout and only includes the content template
|
||||
// 2. The content template specified in Page.Name
|
||||
// 3. All templates within the components directory
|
||||
// Also included is the function map provided by the funcmap package
|
||||
buf, err = c.Container.TemplateRenderer.ParseAndExecute(
|
||||
"page:htmx",
|
||||
page.Name,
|
||||
"htmx",
|
||||
[]string{
|
||||
"htmx",
|
||||
fmt.Sprintf("pages/%s", page.Name),
|
||||
},
|
||||
[]string{"components"},
|
||||
page,
|
||||
)
|
||||
} else {
|
||||
// Parse and execute the templates for the Page
|
||||
// As mentioned in the documentation for the Page struct, the templates used for the page will be:
|
||||
// 1. The layout/base template specified in Page.Layout
|
||||
// 2. The content template specified in Page.Name
|
||||
// 3. All templates within the components directory
|
||||
// Also included is the function map provided by the funcmap package
|
||||
buf, err = c.Container.TemplateRenderer.ParseAndExecute(
|
||||
"page",
|
||||
page.Name,
|
||||
page.Layout,
|
||||
[]string{
|
||||
fmt.Sprintf("layouts/%s", page.Layout),
|
||||
fmt.Sprintf("pages/%s", page.Name),
|
||||
},
|
||||
[]string{"components"},
|
||||
page,
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ctx.Logger().Errorf("failed to parse and execute templates: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Set the status code
|
||||
ctx.Response().Status = page.StatusCode
|
||||
|
||||
// Set any headers
|
||||
for k, v := range page.Headers {
|
||||
ctx.Response().Header().Set(k, v)
|
||||
}
|
||||
|
||||
// Apply the HTMX response, if one
|
||||
if page.HTMX.Response != nil {
|
||||
page.HTMX.Response.Apply(ctx)
|
||||
}
|
||||
|
||||
// Cache this page, if caching was enabled
|
||||
c.cachePage(ctx, page, buf)
|
||||
|
||||
return ctx.HTMLBlob(ctx.Response().Status, buf.Bytes())
|
||||
}
|
||||
|
||||
// cachePage caches the HTML for a given Page if the Page has caching enabled
|
||||
func (c *Controller) cachePage(ctx echo.Context, page Page, html *bytes.Buffer) {
|
||||
if !page.Cache.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
// If no expiration time was provided, default to the configuration value
|
||||
if page.Cache.Expiration == 0 {
|
||||
page.Cache.Expiration = c.Container.Config.Cache.Expiration.Page
|
||||
}
|
||||
|
||||
// Extract the headers
|
||||
headers := make(map[string]string)
|
||||
for k, v := range ctx.Response().Header() {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
|
||||
// The request URL is used as the cache key so the middleware can serve the
|
||||
// cached page on matching requests
|
||||
key := ctx.Request().URL.String()
|
||||
opts := &store.Options{
|
||||
Expiration: page.Cache.Expiration,
|
||||
Tags: page.Cache.Tags,
|
||||
}
|
||||
cp := middleware.CachedPage{
|
||||
URL: key,
|
||||
HTML: html.Bytes(),
|
||||
Headers: headers,
|
||||
StatusCode: ctx.Response().Status,
|
||||
}
|
||||
|
||||
err := marshaler.New(c.Container.Cache).Set(ctx.Request().Context(), key, cp, opts)
|
||||
if err != nil {
|
||||
ctx.Logger().Errorf("failed to cache page: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Logger().Infof("cached page")
|
||||
}
|
||||
|
||||
// Redirect redirects to a given route name with optional route parameters
|
||||
func (c *Controller) Redirect(ctx echo.Context, route string, routeParams ...interface{}) error {
|
||||
url := ctx.Echo().Reverse(route, routeParams)
|
||||
return ctx.Redirect(http.StatusFound, url)
|
||||
}
|
||||
|
||||
// Fail is a helper to fail a request by returning a 500 error and logging the error
|
||||
func (c *Controller) Fail(ctx echo.Context, err error, log string) error {
|
||||
ctx.Logger().Errorf("%s: %v", log, err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"goweb/config"
|
||||
"goweb/htmx"
|
||||
"goweb/middleware"
|
||||
"goweb/services"
|
||||
"goweb/tests"
|
||||
|
||||
"github.com/eko/gocache/v2/store"
|
||||
|
||||
"github.com/eko/gocache/v2/marshaler"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
c *services.Container
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set the environment to test
|
||||
config.SwitchEnvironment(config.EnvTest)
|
||||
|
||||
// Create a new container
|
||||
c = services.NewContainer()
|
||||
defer func() {
|
||||
if err := c.Shutdown(); err != nil {
|
||||
c.Web.Logger.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Run tests
|
||||
exitVal := m.Run()
|
||||
os.Exit(exitVal)
|
||||
}
|
||||
|
||||
func TestController_Redirect(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/abc")
|
||||
ctr := NewController(c)
|
||||
err := ctr.Redirect(ctx, "home")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "", ctx.Response().Header().Get(echo.HeaderLocation))
|
||||
assert.Equal(t, http.StatusFound, ctx.Response().Status)
|
||||
}
|
||||
|
||||
func TestController_RenderPage(t *testing.T) {
|
||||
setup := func() (echo.Context, *httptest.ResponseRecorder, Controller, Page) {
|
||||
ctx, rec := tests.NewContext(c.Web, "/test/TestController_RenderPage")
|
||||
tests.InitSession(ctx)
|
||||
ctr := NewController(c)
|
||||
|
||||
p := NewPage(ctx)
|
||||
p.Name = "home"
|
||||
p.Layout = "main"
|
||||
p.Cache.Enabled = false
|
||||
p.Headers["A"] = "b"
|
||||
p.Headers["C"] = "d"
|
||||
p.StatusCode = http.StatusCreated
|
||||
return ctx, rec, ctr, p
|
||||
}
|
||||
|
||||
t.Run("missing name", func(t *testing.T) {
|
||||
// Rendering should fail if the Page has no name
|
||||
ctx, _, ctr, p := setup()
|
||||
p.Name = ""
|
||||
err := ctr.RenderPage(ctx, p)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("no page cache", func(t *testing.T) {
|
||||
ctx, _, ctr, p := setup()
|
||||
err := ctr.RenderPage(ctx, p)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check status code and headers
|
||||
assert.Equal(t, http.StatusCreated, ctx.Response().Status)
|
||||
for k, v := range p.Headers {
|
||||
assert.Equal(t, v, ctx.Response().Header().Get(k))
|
||||
}
|
||||
|
||||
// Check the template cache
|
||||
parsed, err := c.TemplateRenderer.Load("page", p.Name)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check that all expected templates were parsed.
|
||||
// This includes the name, layout and all components
|
||||
expectedTemplates := make(map[string]bool)
|
||||
expectedTemplates[p.Name+config.TemplateExt] = true
|
||||
expectedTemplates[p.Layout+config.TemplateExt] = true
|
||||
components, err := ioutil.ReadDir(c.TemplateRenderer.GetTemplatesPath() + "/components")
|
||||
require.NoError(t, err)
|
||||
for _, f := range components {
|
||||
expectedTemplates[f.Name()] = true
|
||||
}
|
||||
|
||||
for _, v := range parsed.Templates() {
|
||||
delete(expectedTemplates, v.Name())
|
||||
}
|
||||
assert.Empty(t, expectedTemplates)
|
||||
})
|
||||
|
||||
t.Run("htmx rendering", func(t *testing.T) {
|
||||
ctx, _, ctr, p := setup()
|
||||
p.HTMX.Request.Enabled = true
|
||||
p.HTMX.Response = &htmx.Response{
|
||||
Trigger: "trigger",
|
||||
}
|
||||
err := ctr.RenderPage(ctx, p)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check HTMX header
|
||||
assert.Equal(t, "trigger", ctx.Response().Header().Get(htmx.HeaderTrigger))
|
||||
|
||||
// Check the template cache
|
||||
parsed, err := c.TemplateRenderer.Load("page:htmx", p.Name)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check that all expected templates were parsed.
|
||||
// This includes the name, htmx and all components
|
||||
expectedTemplates := make(map[string]bool)
|
||||
expectedTemplates[p.Name+config.TemplateExt] = true
|
||||
expectedTemplates["htmx"+config.TemplateExt] = true
|
||||
components, err := ioutil.ReadDir(c.TemplateRenderer.GetTemplatesPath() + "/components")
|
||||
require.NoError(t, err)
|
||||
for _, f := range components {
|
||||
expectedTemplates[f.Name()] = true
|
||||
}
|
||||
|
||||
for _, v := range parsed.Templates() {
|
||||
delete(expectedTemplates, v.Name())
|
||||
}
|
||||
assert.Empty(t, expectedTemplates)
|
||||
})
|
||||
|
||||
t.Run("page cache", func(t *testing.T) {
|
||||
ctx, rec, ctr, p := setup()
|
||||
p.Cache.Enabled = true
|
||||
p.Cache.Tags = []string{"tag1"}
|
||||
err := ctr.RenderPage(ctx, p)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Fetch from the cache
|
||||
res, err := marshaler.New(c.Cache).
|
||||
Get(context.Background(), p.URL, new(middleware.CachedPage))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Compare the cached page
|
||||
cp, ok := res.(*middleware.CachedPage)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, p.URL, cp.URL)
|
||||
assert.Equal(t, p.Headers, cp.Headers)
|
||||
assert.Equal(t, p.StatusCode, cp.StatusCode)
|
||||
assert.Equal(t, rec.Body.Bytes(), cp.HTML)
|
||||
|
||||
// Clear the tag
|
||||
err = c.Cache.Invalidate(context.Background(), store.InvalidateOptions{
|
||||
Tags: []string{p.Cache.Tags[0]},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Refetch from the cache and expect no results
|
||||
_, err = marshaler.New(c.Cache).
|
||||
Get(context.Background(), p.URL, new(middleware.CachedPage))
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// FormSubmission represents the state of the submission of a form, not including the form itself
|
||||
type FormSubmission struct {
|
||||
// IsSubmitted indicates if the form has been submitted
|
||||
IsSubmitted bool
|
||||
|
||||
// Errors stores a slice of error message strings keyed by form struct field name
|
||||
Errors map[string][]string
|
||||
}
|
||||
|
||||
// Process processes a submission for a form
|
||||
func (f *FormSubmission) Process(ctx echo.Context, form interface{}) error {
|
||||
f.Errors = make(map[string][]string)
|
||||
f.IsSubmitted = true
|
||||
|
||||
// Validate the form
|
||||
if err := ctx.Validate(form); err != nil {
|
||||
f.setErrorMessages(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasErrors indicates if the submission has any validation errors
|
||||
func (f FormSubmission) HasErrors() bool {
|
||||
if f.Errors == nil {
|
||||
return false
|
||||
}
|
||||
return len(f.Errors) > 0
|
||||
}
|
||||
|
||||
// FieldHasErrors indicates if a given field on the form has any validation errors
|
||||
func (f FormSubmission) FieldHasErrors(fieldName string) bool {
|
||||
return len(f.GetFieldErrors(fieldName)) > 0
|
||||
}
|
||||
|
||||
// SetFieldError sets an error message for a given field name
|
||||
func (f *FormSubmission) SetFieldError(fieldName string, message string) {
|
||||
if f.Errors == nil {
|
||||
f.Errors = make(map[string][]string)
|
||||
}
|
||||
f.Errors[fieldName] = append(f.Errors[fieldName], message)
|
||||
}
|
||||
|
||||
// GetFieldErrors gets the errors for a given field name
|
||||
func (f FormSubmission) GetFieldErrors(fieldName string) []string {
|
||||
if f.Errors == nil {
|
||||
return []string{}
|
||||
}
|
||||
return f.Errors[fieldName]
|
||||
}
|
||||
|
||||
// GetFieldStatusClass returns an HTML class based on the status of the field
|
||||
func (f FormSubmission) GetFieldStatusClass(fieldName string) string {
|
||||
if f.IsSubmitted {
|
||||
if f.FieldHasErrors(fieldName) {
|
||||
return "is-danger"
|
||||
}
|
||||
return "is-success"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsDone indicates if the submission is considered done which is when it has been submitted
|
||||
// and there are no errors.
|
||||
func (f FormSubmission) IsDone() bool {
|
||||
return f.IsSubmitted && !f.HasErrors()
|
||||
}
|
||||
|
||||
// setErrorMessages sets errors messages on the submission for all fields that failed validation
|
||||
func (f *FormSubmission) setErrorMessages(err error) {
|
||||
// Only this is supported right now
|
||||
ves, ok := err.(validator.ValidationErrors)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ve := range ves {
|
||||
var message string
|
||||
|
||||
// Provide better error messages depending on the failed validation tag
|
||||
// This should be expanded as you use additional tags in your validation
|
||||
switch ve.Tag() {
|
||||
case "required":
|
||||
message = "This field is required."
|
||||
case "email":
|
||||
message = "Enter a valid email address."
|
||||
case "eqfield":
|
||||
message = "Does not match."
|
||||
default:
|
||||
message = "Invalid value."
|
||||
}
|
||||
|
||||
// Add the error
|
||||
f.SetFieldError(ve.Field(), message)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"goweb/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFormSubmission(t *testing.T) {
|
||||
type formTest struct {
|
||||
Name string `validate:"required"`
|
||||
Email string `validate:"required,email"`
|
||||
Submission FormSubmission
|
||||
}
|
||||
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
form := formTest{
|
||||
Name: "",
|
||||
Email: "a@a.com",
|
||||
}
|
||||
err := form.Submission.Process(ctx, form)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, form.Submission.HasErrors())
|
||||
assert.True(t, form.Submission.FieldHasErrors("Name"))
|
||||
assert.False(t, form.Submission.FieldHasErrors("Email"))
|
||||
require.Len(t, form.Submission.GetFieldErrors("Name"), 1)
|
||||
assert.Len(t, form.Submission.GetFieldErrors("Email"), 0)
|
||||
assert.Equal(t, "This field is required.", form.Submission.GetFieldErrors("Name")[0])
|
||||
assert.Equal(t, "is-danger", form.Submission.GetFieldStatusClass("Name"))
|
||||
assert.Equal(t, "is-success", form.Submission.GetFieldStatusClass("Email"))
|
||||
assert.False(t, form.Submission.IsDone())
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/ent"
|
||||
"goweb/htmx"
|
||||
"goweb/msg"
|
||||
|
||||
echomw "github.com/labstack/echo/v4/middleware"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Page consists of all data that will be used to render a page response for a given controller.
|
||||
// While it's not required for a controller to render a Page on a route, this is the common data
|
||||
// object that will be passed to the templates, making it easy for all controllers to share
|
||||
// functionality both on the back and frontend. The Page can be expanded to include anything else
|
||||
// your app wants to support.
|
||||
// Methods on this page also then become available in the templates, which can be more useful than
|
||||
// the funcmap if your methods require data stored in the page, such as the context.
|
||||
type Page struct {
|
||||
// AppName stores the name of the application.
|
||||
// If omitted, the configuration value will be used.
|
||||
AppName string
|
||||
|
||||
// Title stores the title of the page
|
||||
Title string
|
||||
|
||||
// Context stores the request context
|
||||
Context echo.Context
|
||||
|
||||
// ToURL is a function to convert a route name and optional route parameters to a URL
|
||||
ToURL func(name string, params ...interface{}) string
|
||||
|
||||
// Path stores the path of the current request
|
||||
Path string
|
||||
|
||||
// URL stores the URL of the current request
|
||||
URL string
|
||||
|
||||
// Data stores whatever additional data that needs to be passed to the templates.
|
||||
// This is what the controller uses to pass the content of the page.
|
||||
Data interface{}
|
||||
|
||||
// Form stores a struct that represents a form on the page.
|
||||
// This should be a struct with fields for each form field, using both "form" and "validate" tags
|
||||
// It should also contain a Submission field of type FormSubmission if you wish to have validation
|
||||
// messagesa and markup presented to the user
|
||||
Form interface{}
|
||||
|
||||
// Layout stores the name of the layout base template file which will be used when the page is rendered.
|
||||
// This should match a template file located within the layouts directory inside the templates directory.
|
||||
// The template extension should not be included in this value.
|
||||
Layout string
|
||||
|
||||
// Name stores the name of the page as well as the name of the template file which will be used to render
|
||||
// the content portion of the layout template.
|
||||
// This should match a template file located within the pages directory inside the templates directory.
|
||||
// The template extension should not be included in this value.
|
||||
Name string
|
||||
|
||||
// IsHome stores whether the requested page is the home page or not
|
||||
IsHome bool
|
||||
|
||||
// IsAuth stores whether or not the user is authenticated
|
||||
IsAuth bool
|
||||
|
||||
// AuthUser stores the authenticated user
|
||||
AuthUser *ent.User
|
||||
|
||||
// StatusCode stores the HTTP status code that will be returned
|
||||
StatusCode int
|
||||
|
||||
// Metatags stores metatag values
|
||||
Metatags struct {
|
||||
// Description stores the description metatag value
|
||||
Description string
|
||||
|
||||
// Keywords stores the keywords metatag values
|
||||
Keywords []string
|
||||
}
|
||||
|
||||
// Pager stores a pager which can be used to page lists of results
|
||||
Pager Pager
|
||||
|
||||
// CSRF stores the CSRF token for the given request.
|
||||
// This will only be populated if the CSRF middleware is in effect for the given request.
|
||||
// If this is populated, all forms must include this value otherwise the requests will be rejected.
|
||||
CSRF string
|
||||
|
||||
// Headers stores a list of HTTP headers and values to be set on the response
|
||||
Headers map[string]string
|
||||
|
||||
// RequestID stores the ID of the given request.
|
||||
// This will only be populated if the request ID middleware is in effect for the given request.
|
||||
RequestID string
|
||||
|
||||
HTMX struct {
|
||||
Request htmx.Request
|
||||
Response *htmx.Response
|
||||
}
|
||||
|
||||
// Cache stores values for caching the response of this page
|
||||
Cache struct {
|
||||
// Enabled dictates if the response of this page should be cached.
|
||||
// Cached responses are served via middleware.
|
||||
Enabled bool
|
||||
|
||||
// Expiration stores the amount of time that the cache entry should live for before expiring.
|
||||
// If omitted, the configuration value will be used.
|
||||
Expiration time.Duration
|
||||
|
||||
// Tags stores a list of tags to apply to the cache entry.
|
||||
// These are useful when invalidating cache for dynamic events such as entity operations.
|
||||
Tags []string
|
||||
}
|
||||
}
|
||||
|
||||
// NewPage creates and initiatizes a new Page for a given request context
|
||||
func NewPage(ctx echo.Context) Page {
|
||||
p := Page{
|
||||
Context: ctx,
|
||||
ToURL: ctx.Echo().Reverse,
|
||||
Path: ctx.Request().URL.Path,
|
||||
URL: ctx.Request().URL.String(),
|
||||
StatusCode: http.StatusOK,
|
||||
Pager: NewPager(ctx, DefaultItemsPerPage),
|
||||
Headers: make(map[string]string),
|
||||
RequestID: ctx.Response().Header().Get(echo.HeaderXRequestID),
|
||||
}
|
||||
|
||||
p.IsHome = p.Path == "/"
|
||||
|
||||
if csrf := ctx.Get(echomw.DefaultCSRFConfig.ContextKey); csrf != nil {
|
||||
p.CSRF = csrf.(string)
|
||||
}
|
||||
|
||||
if u := ctx.Get(context.AuthenticatedUserKey); u != nil {
|
||||
p.IsAuth = true
|
||||
p.AuthUser = u.(*ent.User)
|
||||
}
|
||||
|
||||
p.HTMX.Request = htmx.GetRequest(ctx)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// GetMessages gets all flash messages for a given type.
|
||||
// This allows for easy access to flash messages from the templates.
|
||||
func (p Page) GetMessages(typ msg.Type) []template.HTML {
|
||||
strs := msg.Get(p.Context, typ)
|
||||
ret := make([]template.HTML, len(strs))
|
||||
for k, v := range strs {
|
||||
ret[k] = template.HTML(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/msg"
|
||||
"goweb/tests"
|
||||
|
||||
echomw "github.com/labstack/echo/v4/middleware"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewPage(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
p := NewPage(ctx)
|
||||
assert.Same(t, ctx, p.Context)
|
||||
assert.NotNil(t, p.ToURL)
|
||||
assert.Equal(t, "/", p.Path)
|
||||
assert.Equal(t, "/", p.URL)
|
||||
assert.Equal(t, http.StatusOK, p.StatusCode)
|
||||
assert.Equal(t, NewPager(ctx, DefaultItemsPerPage), p.Pager)
|
||||
assert.Empty(t, p.Headers)
|
||||
assert.True(t, p.IsHome)
|
||||
assert.False(t, p.IsAuth)
|
||||
assert.Empty(t, p.CSRF)
|
||||
assert.Empty(t, p.RequestID)
|
||||
assert.False(t, p.Cache.Enabled)
|
||||
|
||||
ctx, _ = tests.NewContext(c.Web, "/abc?def=123")
|
||||
usr, err := tests.CreateUser(c.ORM)
|
||||
require.NoError(t, err)
|
||||
ctx.Set(context.AuthenticatedUserKey, usr)
|
||||
ctx.Set(echomw.DefaultCSRFConfig.ContextKey, "csrf")
|
||||
p = NewPage(ctx)
|
||||
assert.Equal(t, "/abc", p.Path)
|
||||
assert.Equal(t, "/abc?def=123", p.URL)
|
||||
assert.False(t, p.IsHome)
|
||||
assert.True(t, p.IsAuth)
|
||||
assert.Equal(t, usr, p.AuthUser)
|
||||
assert.Equal(t, "csrf", p.CSRF)
|
||||
}
|
||||
|
||||
func TestPage_GetMessages(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
tests.InitSession(ctx)
|
||||
p := NewPage(ctx)
|
||||
|
||||
// Set messages
|
||||
msgTests := make(map[msg.Type][]string)
|
||||
msgTests[msg.TypeWarning] = []string{
|
||||
"abc",
|
||||
"def",
|
||||
}
|
||||
msgTests[msg.TypeInfo] = []string{
|
||||
"123",
|
||||
"456",
|
||||
}
|
||||
for typ, values := range msgTests {
|
||||
for _, value := range values {
|
||||
msg.Set(ctx, typ, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the messages
|
||||
for typ, values := range msgTests {
|
||||
msgs := p.GetMessages(typ)
|
||||
|
||||
for i, message := range msgs {
|
||||
assert.Equal(t, values[i], string(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
cache:
|
||||
image: "redis:alpine"
|
||||
ports:
|
||||
- "6379:6379"
|
||||
db:
|
||||
image: postgres:alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=admin
|
||||
- POSTGRES_PASSWORD=admin
|
||||
- POSTGRES_DB=app
|
||||
97
ent/admin/extension.go
Normal file
97
ent/admin/extension.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
"entgo.io/ent/entc"
|
||||
"entgo.io/ent/entc/gen"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed templates
|
||||
templateDir embed.FS
|
||||
)
|
||||
|
||||
// Extension is the Ent extension that generates code to support the entity admin panel.
|
||||
type Extension struct {
|
||||
entc.DefaultExtension
|
||||
}
|
||||
|
||||
func (*Extension) Templates() []*gen.Template {
|
||||
return []*gen.Template{
|
||||
gen.MustParse(
|
||||
gen.NewTemplate("admin").
|
||||
Funcs(template.FuncMap{
|
||||
"fieldName": fieldName,
|
||||
"fieldLabel": FieldLabel,
|
||||
"fieldIsPointer": fieldIsPointer,
|
||||
}).
|
||||
ParseFS(templateDir, "templates/*tmpl"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// fieldName provides a struct field name from an entity field name (ie, user_id -> UserID).
|
||||
func fieldName(name string) string {
|
||||
if len(name) == 0 {
|
||||
return name
|
||||
}
|
||||
|
||||
parts := strings.Split(name, "_")
|
||||
for i := 0; i < len(parts); i++ {
|
||||
if parts[i] == "id" {
|
||||
parts[i] = "ID"
|
||||
} else {
|
||||
parts[i] = upperFirst(parts[i])
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(parts, "")
|
||||
}
|
||||
|
||||
// FieldLabel provides a label for an entity field name (ie, user_id -> User ID).
|
||||
func FieldLabel(name string) string {
|
||||
if len(name) == 0 {
|
||||
return name
|
||||
}
|
||||
|
||||
parts := strings.Split(name, "_")
|
||||
for i := 0; i < len(parts); i++ {
|
||||
if parts[i] == "id" {
|
||||
parts[i] = "ID"
|
||||
}
|
||||
if i == 0 {
|
||||
parts[i] = upperFirst(parts[i])
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// fieldIsPointer determines if a given entity field should be a pointer on the struct.
|
||||
func fieldIsPointer(f *gen.Field) bool {
|
||||
switch {
|
||||
case f.Type.Type == field.TypeBool:
|
||||
return false
|
||||
case f.Optional,
|
||||
f.Default,
|
||||
f.Sensitive(),
|
||||
f.Nillable:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// upperFirst uppercases the first character of a given string.
|
||||
func upperFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
out := []rune(s)
|
||||
out[0] = unicode.ToUpper(out[0])
|
||||
return string(out)
|
||||
}
|
||||
319
ent/admin/handler.go
Normal file
319
ent/admin/handler.go
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
// Code generated by ent, DO NOT EDIT.
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
const dateTimeFormat = "2006-01-02T15:04:05"
|
||||
const dateTimeFormatNoSeconds = "2006-01-02T15:04"
|
||||
|
||||
type Handler struct {
|
||||
client *ent.Client
|
||||
Config HandlerConfig
|
||||
}
|
||||
|
||||
func NewHandler(client *ent.Client, cfg HandlerConfig) *Handler {
|
||||
return &Handler{
|
||||
client: client,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Create(ctx echo.Context, entityType EntityType) error {
|
||||
switch entityType.(type) {
|
||||
case *PasswordToken:
|
||||
return h.PasswordTokenCreate(ctx)
|
||||
case *User:
|
||||
return h.UserCreate(ctx)
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) {
|
||||
switch entityType.(type) {
|
||||
case *PasswordToken:
|
||||
return h.PasswordTokenGet(ctx, id)
|
||||
case *User:
|
||||
return h.UserGet(ctx, id)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error {
|
||||
switch entityType.(type) {
|
||||
case *PasswordToken:
|
||||
return h.PasswordTokenDelete(ctx, id)
|
||||
case *User:
|
||||
return h.UserDelete(ctx, id)
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error {
|
||||
switch entityType.(type) {
|
||||
case *PasswordToken:
|
||||
return h.PasswordTokenUpdate(ctx, id)
|
||||
case *User:
|
||||
return h.UserUpdate(ctx, id)
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) {
|
||||
switch entityType.(type) {
|
||||
case *PasswordToken:
|
||||
return h.PasswordTokenList(ctx)
|
||||
case *User:
|
||||
return h.UserList(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) PasswordTokenCreate(ctx echo.Context) error {
|
||||
var payload PasswordToken
|
||||
if err := h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := h.client.PasswordToken.Create()
|
||||
if payload.Token != nil {
|
||||
op.SetToken(*payload.Token)
|
||||
}
|
||||
op.SetUserID(payload.UserID)
|
||||
if payload.CreatedAt != nil {
|
||||
op.SetCreatedAt(*payload.CreatedAt)
|
||||
}
|
||||
_, err := op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) PasswordTokenUpdate(ctx echo.Context, id int) error {
|
||||
entity, err := h.client.PasswordToken.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var payload PasswordToken
|
||||
if err = h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := entity.Update()
|
||||
if payload.Token != nil {
|
||||
op.SetToken(*payload.Token)
|
||||
}
|
||||
op.SetUserID(payload.UserID)
|
||||
if payload.CreatedAt == nil {
|
||||
var empty time.Time
|
||||
op.SetCreatedAt(empty)
|
||||
} else {
|
||||
op.SetCreatedAt(*payload.CreatedAt)
|
||||
}
|
||||
_, err = op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) PasswordTokenDelete(ctx echo.Context, id int) error {
|
||||
return h.client.PasswordToken.DeleteOneID(id).
|
||||
Exec(ctx.Request().Context())
|
||||
}
|
||||
|
||||
func (h *Handler) PasswordTokenList(ctx echo.Context) (*EntityList, error) {
|
||||
page, offset := h.getPageAndOffset(ctx)
|
||||
res, err := h.client.PasswordToken.
|
||||
Query().
|
||||
Limit(h.Config.ItemsPerPage + 1).
|
||||
Offset(offset).
|
||||
Order(passwordtoken.ByID(sql.OrderDesc())).
|
||||
All(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &EntityList{
|
||||
Columns: []string{
|
||||
"User ID",
|
||||
"Created at",
|
||||
},
|
||||
Entities: make([]EntityValues, 0, len(res)),
|
||||
Page: page,
|
||||
HasNextPage: len(res) > h.Config.ItemsPerPage,
|
||||
}
|
||||
|
||||
for i := 0; i <= len(res)-1; i++ {
|
||||
list.Entities = append(list.Entities, EntityValues{
|
||||
ID: res[i].ID,
|
||||
Values: []string{
|
||||
fmt.Sprint(res[i].UserID),
|
||||
res[i].CreatedAt.Format(h.Config.TimeFormat),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (h *Handler) PasswordTokenGet(ctx echo.Context, id int) (url.Values, error) {
|
||||
entity, err := h.client.PasswordToken.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("user_id", fmt.Sprint(entity.UserID))
|
||||
v.Set("created_at", entity.CreatedAt.Format(dateTimeFormat))
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (h *Handler) UserCreate(ctx echo.Context) error {
|
||||
var payload User
|
||||
if err := h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := h.client.User.Create()
|
||||
op.SetName(payload.Name)
|
||||
op.SetEmail(payload.Email)
|
||||
if payload.Password != nil {
|
||||
op.SetPassword(*payload.Password)
|
||||
}
|
||||
op.SetVerified(payload.Verified)
|
||||
op.SetAdmin(payload.Admin)
|
||||
if payload.CreatedAt != nil {
|
||||
op.SetCreatedAt(*payload.CreatedAt)
|
||||
}
|
||||
_, err := op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) UserUpdate(ctx echo.Context, id int) error {
|
||||
entity, err := h.client.User.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var payload User
|
||||
if err = h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := entity.Update()
|
||||
op.SetName(payload.Name)
|
||||
op.SetEmail(payload.Email)
|
||||
if payload.Password != nil {
|
||||
op.SetPassword(*payload.Password)
|
||||
}
|
||||
op.SetVerified(payload.Verified)
|
||||
op.SetAdmin(payload.Admin)
|
||||
_, err = op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) UserDelete(ctx echo.Context, id int) error {
|
||||
return h.client.User.DeleteOneID(id).
|
||||
Exec(ctx.Request().Context())
|
||||
}
|
||||
|
||||
func (h *Handler) UserList(ctx echo.Context) (*EntityList, error) {
|
||||
page, offset := h.getPageAndOffset(ctx)
|
||||
res, err := h.client.User.
|
||||
Query().
|
||||
Limit(h.Config.ItemsPerPage + 1).
|
||||
Offset(offset).
|
||||
Order(user.ByID(sql.OrderDesc())).
|
||||
All(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &EntityList{
|
||||
Columns: []string{
|
||||
"Name",
|
||||
"Email",
|
||||
"Verified",
|
||||
"Admin",
|
||||
"Created at",
|
||||
},
|
||||
Entities: make([]EntityValues, 0, len(res)),
|
||||
Page: page,
|
||||
HasNextPage: len(res) > h.Config.ItemsPerPage,
|
||||
}
|
||||
|
||||
for i := 0; i <= len(res)-1; i++ {
|
||||
list.Entities = append(list.Entities, EntityValues{
|
||||
ID: res[i].ID,
|
||||
Values: []string{
|
||||
res[i].Name,
|
||||
res[i].Email,
|
||||
fmt.Sprint(res[i].Verified),
|
||||
fmt.Sprint(res[i].Admin),
|
||||
res[i].CreatedAt.Format(h.Config.TimeFormat),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (h *Handler) UserGet(ctx echo.Context, id int) (url.Values, error) {
|
||||
entity, err := h.client.User.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("name", entity.Name)
|
||||
v.Set("email", entity.Email)
|
||||
v.Set("verified", fmt.Sprint(entity.Verified))
|
||||
v.Set("admin", fmt.Sprint(entity.Admin))
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (h *Handler) getPageAndOffset(ctx echo.Context) (int, int) {
|
||||
if page, err := strconv.Atoi(ctx.QueryParam(h.Config.PageQueryKey)); err == nil {
|
||||
if page > 1 {
|
||||
return page, (page - 1) * h.Config.ItemsPerPage
|
||||
}
|
||||
}
|
||||
return 1, 0
|
||||
}
|
||||
|
||||
func (h *Handler) bind(ctx echo.Context, entity any) error {
|
||||
// Echo requires some pre-processing of form values to avoid problems.
|
||||
for k, v := range ctx.Request().Form {
|
||||
// Remove empty field values so Echo's bind does not fail when trying to parse things like
|
||||
// times, etc.
|
||||
if len(v) == 1 && len(v[0]) == 0 {
|
||||
delete(ctx.Request().Form, k)
|
||||
continue
|
||||
}
|
||||
|
||||
// Echo expects datetime values to be in a certain format but that does not align with the datetime-local
|
||||
// HTML form element format, so we will attempt to convert it here.
|
||||
for _, format := range []string{dateTimeFormatNoSeconds, dateTimeFormat} {
|
||||
if t, err := time.Parse(format, v[0]); err == nil {
|
||||
ctx.Request().Form[k][0] = t.Format(time.RFC3339)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx.Bind(entity)
|
||||
}
|
||||
101
ent/admin/schema.go
Normal file
101
ent/admin/schema.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Code generated by ent, DO NOT EDIT.
|
||||
package admin
|
||||
|
||||
import (
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
type Enum struct {
|
||||
Label, Value string
|
||||
}
|
||||
|
||||
type FieldSchema struct {
|
||||
Name string
|
||||
Type field.Type
|
||||
Optional bool
|
||||
Immutable bool
|
||||
Sensitive bool
|
||||
Enums []string
|
||||
}
|
||||
|
||||
const NamePasswordToken = "PasswordToken"
|
||||
|
||||
var fieldsPasswordToken = []*FieldSchema{
|
||||
{
|
||||
Name: "token",
|
||||
Type: field.TypeString,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: true,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "user_id",
|
||||
Type: field.TypeInt,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "created_at",
|
||||
Type: field.TypeTime,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
}
|
||||
|
||||
const NameUser = "User"
|
||||
|
||||
var fieldsUser = []*FieldSchema{
|
||||
{
|
||||
Name: "name",
|
||||
Type: field.TypeString,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "email",
|
||||
Type: field.TypeString,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "password",
|
||||
Type: field.TypeString,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: true,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "verified",
|
||||
Type: field.TypeBool,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "admin",
|
||||
Type: field.TypeBool,
|
||||
Optional: false,
|
||||
Immutable: false,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
{
|
||||
Name: "created_at",
|
||||
Type: field.TypeTime,
|
||||
Optional: false,
|
||||
Immutable: true,
|
||||
Sensitive: false,
|
||||
Enums: nil,
|
||||
},
|
||||
}
|
||||
262
ent/admin/templates/handler.tmpl
Normal file
262
ent/admin/templates/handler.tmpl
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
|
||||
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
|
||||
|
||||
{{ define "admin/handler" }}
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
{{- $pkg := base $.Config.Package }}
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"{{ $.Config.Package }}"
|
||||
{{- range $n := $.Nodes }}
|
||||
"{{ $.Config.Package }}/{{ $n.Package }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
const dateTimeFormat = "2006-01-02T15:04:05"
|
||||
const dateTimeFormatNoSeconds = "2006-01-02T15:04"
|
||||
|
||||
type Handler struct {
|
||||
client *{{ $pkg }}.Client
|
||||
Config HandlerConfig
|
||||
}
|
||||
|
||||
func NewHandler(client *{{ $pkg }}.Client, cfg HandlerConfig) *Handler {
|
||||
return &Handler{
|
||||
client: client,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Create(ctx echo.Context, entityType EntityType) error {
|
||||
switch entityType.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $n.Name }}:
|
||||
return h.{{ $n.Name }}Create(ctx)
|
||||
{{- end }}
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) {
|
||||
switch entityType.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $n.Name }}:
|
||||
return h.{{ $n.Name }}Get(ctx, id)
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error {
|
||||
switch entityType.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $n.Name }}:
|
||||
return h.{{ $n.Name }}Delete(ctx, id)
|
||||
{{- end }}
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error {
|
||||
switch entityType.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $n.Name }}:
|
||||
return h.{{ $n.Name }}Update(ctx, id)
|
||||
{{- end }}
|
||||
default:
|
||||
return fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) {
|
||||
switch entityType.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $n.Name }}:
|
||||
return h.{{ $n.Name }}List(ctx)
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
|
||||
}
|
||||
}
|
||||
|
||||
{{ range $n := $.Nodes }}
|
||||
func (h *Handler) {{ $n.Name }}Create(ctx echo.Context) error {
|
||||
var payload {{ $n.Name }}
|
||||
if err := h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := h.client.{{ $n.Name }}.Create()
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if (fieldIsPointer $f) }}
|
||||
if payload.{{ fieldName $f.Name }} != nil {
|
||||
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
|
||||
}
|
||||
{{- else }}
|
||||
op.Set{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
_, err := op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) {{ $n.Name }}Update(ctx echo.Context, id int) error {
|
||||
entity, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var payload {{ $n.Name }}
|
||||
if err = h.bind(ctx, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := entity.Update()
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if not $f.Immutable }}
|
||||
{{- if $f.Sensitive }}
|
||||
if payload.{{ fieldName $f.Name }} != nil {
|
||||
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
|
||||
}
|
||||
{{- else if $f.Nillable }}
|
||||
op.SetNillable{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
|
||||
{{- else if $f.Optional }}
|
||||
if payload.{{ fieldName $f.Name }} == nil {
|
||||
op.Clear{{ fieldName $f.Name }}()
|
||||
} else {
|
||||
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
|
||||
}
|
||||
{{- else if (fieldIsPointer $f) }}
|
||||
if payload.{{ fieldName $f.Name }} == nil {
|
||||
var empty {{ $f.Type }}
|
||||
op.Set{{ fieldName $f.Name }}(empty)
|
||||
} else {
|
||||
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
|
||||
}
|
||||
{{- else }}
|
||||
op.Set{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
_, err = op.Save(ctx.Request().Context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) {{ $n.Name }}Delete(ctx echo.Context, id int) error {
|
||||
return h.client.{{ $n.Name }}.DeleteOneID(id).
|
||||
Exec(ctx.Request().Context())
|
||||
}
|
||||
|
||||
func (h *Handler) {{ $n.Name }}List(ctx echo.Context) (*EntityList, error) {
|
||||
page, offset := h.getPageAndOffset(ctx)
|
||||
res, err := h.client.{{ $n.Name }}.
|
||||
Query().
|
||||
Limit(h.Config.ItemsPerPage+1).
|
||||
Offset(offset).
|
||||
Order({{ $n.Package }}.ByID(sql.OrderDesc())).
|
||||
All(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &EntityList{
|
||||
Columns: []string{
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if not $f.Sensitive }}
|
||||
"{{ fieldLabel $f.Name }}",
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
Entities: make([]EntityValues, 0, len(res)),
|
||||
Page: page,
|
||||
HasNextPage: len(res) > h.Config.ItemsPerPage,
|
||||
}
|
||||
|
||||
for i := 0; i <= len(res)-1; i++ {
|
||||
list.Entities = append(list.Entities, EntityValues{
|
||||
ID: res[i].ID,
|
||||
Values: []string{
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if not $f.Sensitive }}
|
||||
{{- if eq $f.Type.String "string" }}
|
||||
res[i].{{ fieldName $f.Name }},
|
||||
{{- else if eq $f.Type.String "time.Time" }}
|
||||
res[i].{{ fieldName $f.Name }}.Format(h.Config.TimeFormat),
|
||||
{{- else }}
|
||||
fmt.Sprint(res[i].{{ fieldName $f.Name }}),
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (h *Handler) {{ $n.Name }}Get(ctx echo.Context, id int) (url.Values, error) {
|
||||
entity, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if and (not $f.Sensitive) (not $f.Immutable) }}
|
||||
{{- if eq $f.Type.String "string" }}
|
||||
v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }})
|
||||
{{- else if eq $f.Type.String "time.Time" }}
|
||||
v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }}.Format(dateTimeFormat))
|
||||
{{- else }}
|
||||
v.Set("{{ $f.Name }}", fmt.Sprint(entity.{{ fieldName $f.Name }}))
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return v, err
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (h *Handler) getPageAndOffset(ctx echo.Context) (int, int) {
|
||||
if page, err := strconv.Atoi(ctx.QueryParam(h.Config.PageQueryKey)); err == nil {
|
||||
if page > 1 {
|
||||
return page, (page-1) * h.Config.ItemsPerPage
|
||||
}
|
||||
}
|
||||
return 1, 0
|
||||
}
|
||||
|
||||
func (h *Handler) bind(ctx echo.Context, entity any) error {
|
||||
// Echo requires some pre-processing of form values to avoid problems.
|
||||
for k, v := range ctx.Request().Form {
|
||||
// Remove empty field values so Echo's bind does not fail when trying to parse things like
|
||||
// times, etc.
|
||||
if len(v) == 1 && len(v[0]) == 0 {
|
||||
delete(ctx.Request().Form, k)
|
||||
continue
|
||||
}
|
||||
|
||||
// Echo expects datetime values to be in a certain format but that does not align with the datetime-local
|
||||
// HTML form element format, so we will attempt to convert it here.
|
||||
for _, format := range []string{dateTimeFormatNoSeconds, dateTimeFormat} {
|
||||
if t, err := time.Parse(format, v[0]); err == nil {
|
||||
ctx.Request().Form[k][0] = t.Format(time.RFC3339)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx.Bind(entity)
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
51
ent/admin/templates/schema.tmpl
Normal file
51
ent/admin/templates/schema.tmpl
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
|
||||
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
|
||||
|
||||
{{ define "admin/schema" }}
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
package admin
|
||||
|
||||
import (
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
type Enum struct {
|
||||
Label, Value string
|
||||
}
|
||||
|
||||
type FieldSchema struct {
|
||||
Name string
|
||||
Type field.Type
|
||||
Optional bool
|
||||
Immutable bool
|
||||
Sensitive bool
|
||||
Enums []string
|
||||
}
|
||||
|
||||
|
||||
{{- range $n := $.Nodes }}
|
||||
const Name{{ $n.Name }} = "{{ $n.Name }}"
|
||||
|
||||
var fields{{ $n.Name }} = []*FieldSchema{
|
||||
{{- range $f := $n.Fields }}
|
||||
{
|
||||
Name: "{{ $f.Name }}",
|
||||
Type: field.{{ $f.Type.Type.ConstName }},
|
||||
Optional: {{ $f.Optional }},
|
||||
Immutable: {{ $f.Immutable }},
|
||||
Sensitive: {{ $f.Sensitive }},
|
||||
{{- if len $f.Enums }}
|
||||
Enums: []string{
|
||||
{{- range $e := $f.Enums }}
|
||||
"{{ $e.Value }}",
|
||||
{{- end }}
|
||||
},
|
||||
{{- else }}
|
||||
Enums: nil,
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
56
ent/admin/templates/types.tmpl
Normal file
56
ent/admin/templates/types.tmpl
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
|
||||
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
|
||||
|
||||
{{ define "admin/types" }}
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
package admin
|
||||
|
||||
{{- range $n := $.Nodes }}
|
||||
type {{ $n.Name }} struct {
|
||||
{{- range $f := $n.Fields }}
|
||||
{{ fieldName $f.Name }} {{ if (fieldIsPointer $f) }}*{{ end }}{{ $f.Type }} `form:"{{ $f.Name }}"`
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
func (e *{{ $n.Name }}) GetName() string {
|
||||
return Name{{ $n.Name }}
|
||||
}
|
||||
|
||||
func (e *{{ $n.Name }}) GetSchema() []*FieldSchema {
|
||||
return fields{{ $n.Name }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
type EntityType interface {
|
||||
GetName() string
|
||||
GetSchema() []*FieldSchema
|
||||
}
|
||||
|
||||
var entityTypes = []EntityType{
|
||||
{{- range $n := $.Nodes }}
|
||||
&{{ $n.Name }}{},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
type EntityList struct {
|
||||
Columns []string
|
||||
Entities []EntityValues
|
||||
Page int
|
||||
HasNextPage bool
|
||||
}
|
||||
|
||||
type EntityValues struct {
|
||||
ID int
|
||||
Values []string
|
||||
}
|
||||
|
||||
type HandlerConfig struct {
|
||||
ItemsPerPage int
|
||||
PageQueryKey string
|
||||
TimeFormat string
|
||||
}
|
||||
|
||||
func GetEntityTypes() []EntityType {
|
||||
return entityTypes
|
||||
}
|
||||
{{ end }}
|
||||
67
ent/admin/types.go
Normal file
67
ent/admin/types.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Code generated by ent, DO NOT EDIT.
|
||||
package admin
|
||||
|
||||
import "time"
|
||||
|
||||
type PasswordToken struct {
|
||||
Token *string `form:"token"`
|
||||
UserID int `form:"user_id"`
|
||||
CreatedAt *time.Time `form:"created_at"`
|
||||
}
|
||||
|
||||
func (e *PasswordToken) GetName() string {
|
||||
return NamePasswordToken
|
||||
}
|
||||
|
||||
func (e *PasswordToken) GetSchema() []*FieldSchema {
|
||||
return fieldsPasswordToken
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `form:"name"`
|
||||
Email string `form:"email"`
|
||||
Password *string `form:"password"`
|
||||
Verified bool `form:"verified"`
|
||||
Admin bool `form:"admin"`
|
||||
CreatedAt *time.Time `form:"created_at"`
|
||||
}
|
||||
|
||||
func (e *User) GetName() string {
|
||||
return NameUser
|
||||
}
|
||||
|
||||
func (e *User) GetSchema() []*FieldSchema {
|
||||
return fieldsUser
|
||||
}
|
||||
|
||||
type EntityType interface {
|
||||
GetName() string
|
||||
GetSchema() []*FieldSchema
|
||||
}
|
||||
|
||||
var entityTypes = []EntityType{
|
||||
&PasswordToken{},
|
||||
&User{},
|
||||
}
|
||||
|
||||
type EntityList struct {
|
||||
Columns []string
|
||||
Entities []EntityValues
|
||||
Page int
|
||||
HasNextPage bool
|
||||
}
|
||||
|
||||
type EntityValues struct {
|
||||
ID int
|
||||
Values []string
|
||||
}
|
||||
|
||||
type HandlerConfig struct {
|
||||
ItemsPerPage int
|
||||
PageQueryKey string
|
||||
TimeFormat string
|
||||
}
|
||||
|
||||
func GetEntityTypes() []EntityType {
|
||||
return entityTypes
|
||||
}
|
||||
247
ent/client.go
247
ent/client.go
|
|
@ -1,20 +1,22 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"goweb/ent/migrate"
|
||||
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/user"
|
||||
"github.com/mikestefanello/pagoda/ent/migrate"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// Client is the client that holds all ent builders.
|
||||
|
|
@ -30,9 +32,7 @@ type Client struct {
|
|||
|
||||
// NewClient creates a new client configured with the given options.
|
||||
func NewClient(opts ...Option) *Client {
|
||||
cfg := config{log: log.Println, hooks: &hooks{}}
|
||||
cfg.options(opts...)
|
||||
client := &Client{config: cfg}
|
||||
client := &Client{config: newConfig(opts...)}
|
||||
client.init()
|
||||
return client
|
||||
}
|
||||
|
|
@ -43,6 +43,62 @@ func (c *Client) init() {
|
|||
c.User = NewUserClient(c.config)
|
||||
}
|
||||
|
||||
type (
|
||||
// config is the configuration for the client and its builder.
|
||||
config struct {
|
||||
// driver used for executing database requests.
|
||||
driver dialect.Driver
|
||||
// debug enable a debug logging.
|
||||
debug bool
|
||||
// log used for logging on debug mode.
|
||||
log func(...any)
|
||||
// hooks to execute on mutations.
|
||||
hooks *hooks
|
||||
// interceptors to execute on queries.
|
||||
inters *inters
|
||||
}
|
||||
// Option function to configure the client.
|
||||
Option func(*config)
|
||||
)
|
||||
|
||||
// newConfig creates a new config for the client.
|
||||
func newConfig(opts ...Option) config {
|
||||
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
|
||||
cfg.options(opts...)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// options applies the options on the config object.
|
||||
func (c *config) options(opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
if c.debug {
|
||||
c.driver = dialect.Debug(c.driver, c.log)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug enables debug logging on the ent.Driver.
|
||||
func Debug() Option {
|
||||
return func(c *config) {
|
||||
c.debug = true
|
||||
}
|
||||
}
|
||||
|
||||
// Log sets the logging function for debug mode.
|
||||
func Log(fn func(...any)) Option {
|
||||
return func(c *config) {
|
||||
c.log = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Driver configures the client driver.
|
||||
func Driver(driver dialect.Driver) Option {
|
||||
return func(c *config) {
|
||||
c.driver = driver
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens a database/sql.DB specified by the driver name and
|
||||
// the data source name, and returns a new client attached to it.
|
||||
// Optional parameters can be added for configuring the client.
|
||||
|
|
@ -59,11 +115,14 @@ func Open(driverName, dataSourceName string, options ...Option) (*Client, error)
|
|||
}
|
||||
}
|
||||
|
||||
// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
|
||||
var ErrTxStarted = errors.New("ent: cannot start a transaction within a transaction")
|
||||
|
||||
// Tx returns a new transactional client. The provided context
|
||||
// is used until the transaction is committed or rolled back.
|
||||
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
||||
if _, ok := c.driver.(*txDriver); ok {
|
||||
return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
|
||||
return nil, ErrTxStarted
|
||||
}
|
||||
tx, err := newTx(ctx, c.driver)
|
||||
if err != nil {
|
||||
|
|
@ -82,7 +141,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
|||
// BeginTx returns a transactional client with specified options.
|
||||
func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
|
||||
if _, ok := c.driver.(*txDriver); ok {
|
||||
return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
|
||||
return nil, errors.New("ent: cannot start a transaction within a transaction")
|
||||
}
|
||||
tx, err := c.driver.(interface {
|
||||
BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error)
|
||||
|
|
@ -93,6 +152,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
|
|||
cfg := c.config
|
||||
cfg.driver = &txDriver{tx: tx, drv: c.driver}
|
||||
return &Tx{
|
||||
ctx: ctx,
|
||||
config: cfg,
|
||||
PasswordToken: NewPasswordTokenClient(cfg),
|
||||
User: NewUserClient(cfg),
|
||||
|
|
@ -105,7 +165,6 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
|
|||
// PasswordToken.
|
||||
// Query().
|
||||
// Count(ctx)
|
||||
//
|
||||
func (c *Client) Debug() *Client {
|
||||
if c.debug {
|
||||
return c
|
||||
|
|
@ -129,6 +188,25 @@ func (c *Client) Use(hooks ...Hook) {
|
|||
c.User.Use(hooks...)
|
||||
}
|
||||
|
||||
// Intercept adds the query interceptors to all the entity clients.
|
||||
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
|
||||
func (c *Client) Intercept(interceptors ...Interceptor) {
|
||||
c.PasswordToken.Intercept(interceptors...)
|
||||
c.User.Intercept(interceptors...)
|
||||
}
|
||||
|
||||
// Mutate implements the ent.Mutator interface.
|
||||
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
|
||||
switch m := m.(type) {
|
||||
case *PasswordTokenMutation:
|
||||
return c.PasswordToken.mutate(ctx, m)
|
||||
case *UserMutation:
|
||||
return c.User.mutate(ctx, m)
|
||||
default:
|
||||
return nil, fmt.Errorf("ent: unknown mutation type %T", m)
|
||||
}
|
||||
}
|
||||
|
||||
// PasswordTokenClient is a client for the PasswordToken schema.
|
||||
type PasswordTokenClient struct {
|
||||
config
|
||||
|
|
@ -145,7 +223,13 @@ func (c *PasswordTokenClient) Use(hooks ...Hook) {
|
|||
c.hooks.PasswordToken = append(c.hooks.PasswordToken, hooks...)
|
||||
}
|
||||
|
||||
// Create returns a create builder for PasswordToken.
|
||||
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||
// A call to `Intercept(f, g, h)` equals to `passwordtoken.Intercept(f(g(h())))`.
|
||||
func (c *PasswordTokenClient) Intercept(interceptors ...Interceptor) {
|
||||
c.inters.PasswordToken = append(c.inters.PasswordToken, interceptors...)
|
||||
}
|
||||
|
||||
// Create returns a builder for creating a PasswordToken entity.
|
||||
func (c *PasswordTokenClient) Create() *PasswordTokenCreate {
|
||||
mutation := newPasswordTokenMutation(c.config, OpCreate)
|
||||
return &PasswordTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
|
|
@ -156,6 +240,21 @@ func (c *PasswordTokenClient) CreateBulk(builders ...*PasswordTokenCreate) *Pass
|
|||
return &PasswordTokenCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *PasswordTokenClient) MapCreateBulk(slice any, setFunc func(*PasswordTokenCreate, int)) *PasswordTokenCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &PasswordTokenCreateBulk{err: fmt.Errorf("calling to PasswordTokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*PasswordTokenCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &PasswordTokenCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for PasswordToken.
|
||||
func (c *PasswordTokenClient) Update() *PasswordTokenUpdate {
|
||||
mutation := newPasswordTokenMutation(c.config, OpUpdate)
|
||||
|
|
@ -163,8 +262,8 @@ func (c *PasswordTokenClient) Update() *PasswordTokenUpdate {
|
|||
}
|
||||
|
||||
// UpdateOne returns an update builder for the given entity.
|
||||
func (c *PasswordTokenClient) UpdateOne(pt *PasswordToken) *PasswordTokenUpdateOne {
|
||||
mutation := newPasswordTokenMutation(c.config, OpUpdateOne, withPasswordToken(pt))
|
||||
func (c *PasswordTokenClient) UpdateOne(_m *PasswordToken) *PasswordTokenUpdateOne {
|
||||
mutation := newPasswordTokenMutation(c.config, OpUpdateOne, withPasswordToken(_m))
|
||||
return &PasswordTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
|
|
@ -180,12 +279,12 @@ func (c *PasswordTokenClient) Delete() *PasswordTokenDelete {
|
|||
return &PasswordTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// DeleteOne returns a delete builder for the given entity.
|
||||
func (c *PasswordTokenClient) DeleteOne(pt *PasswordToken) *PasswordTokenDeleteOne {
|
||||
return c.DeleteOneID(pt.ID)
|
||||
// DeleteOne returns a builder for deleting the given entity.
|
||||
func (c *PasswordTokenClient) DeleteOne(_m *PasswordToken) *PasswordTokenDeleteOne {
|
||||
return c.DeleteOneID(_m.ID)
|
||||
}
|
||||
|
||||
// DeleteOneID returns a delete builder for the given id.
|
||||
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||
func (c *PasswordTokenClient) DeleteOneID(id int) *PasswordTokenDeleteOne {
|
||||
builder := c.Delete().Where(passwordtoken.ID(id))
|
||||
builder.mutation.id = &id
|
||||
|
|
@ -197,6 +296,8 @@ func (c *PasswordTokenClient) DeleteOneID(id int) *PasswordTokenDeleteOne {
|
|||
func (c *PasswordTokenClient) Query() *PasswordTokenQuery {
|
||||
return &PasswordTokenQuery{
|
||||
config: c.config,
|
||||
ctx: &QueryContext{Type: TypePasswordToken},
|
||||
inters: c.Interceptors(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,16 +316,16 @@ func (c *PasswordTokenClient) GetX(ctx context.Context, id int) *PasswordToken {
|
|||
}
|
||||
|
||||
// QueryUser queries the user edge of a PasswordToken.
|
||||
func (c *PasswordTokenClient) QueryUser(pt *PasswordToken) *UserQuery {
|
||||
query := &UserQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := pt.ID
|
||||
func (c *PasswordTokenClient) QueryUser(_m *PasswordToken) *UserQuery {
|
||||
query := (&UserClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := _m.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(passwordtoken.Table, passwordtoken.FieldID, id),
|
||||
sqlgraph.To(user.Table, user.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, passwordtoken.UserTable, passwordtoken.UserColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(pt.driver.Dialect(), step)
|
||||
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
|
|
@ -232,7 +333,28 @@ func (c *PasswordTokenClient) QueryUser(pt *PasswordToken) *UserQuery {
|
|||
|
||||
// Hooks returns the client hooks.
|
||||
func (c *PasswordTokenClient) Hooks() []Hook {
|
||||
return c.hooks.PasswordToken
|
||||
hooks := c.hooks.PasswordToken
|
||||
return append(hooks[:len(hooks):len(hooks)], passwordtoken.Hooks[:]...)
|
||||
}
|
||||
|
||||
// Interceptors returns the client interceptors.
|
||||
func (c *PasswordTokenClient) Interceptors() []Interceptor {
|
||||
return c.inters.PasswordToken
|
||||
}
|
||||
|
||||
func (c *PasswordTokenClient) mutate(ctx context.Context, m *PasswordTokenMutation) (Value, error) {
|
||||
switch m.Op() {
|
||||
case OpCreate:
|
||||
return (&PasswordTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdate:
|
||||
return (&PasswordTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdateOne:
|
||||
return (&PasswordTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpDelete, OpDeleteOne:
|
||||
return (&PasswordTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("ent: unknown PasswordToken mutation op: %q", m.Op())
|
||||
}
|
||||
}
|
||||
|
||||
// UserClient is a client for the User schema.
|
||||
|
|
@ -251,7 +373,13 @@ func (c *UserClient) Use(hooks ...Hook) {
|
|||
c.hooks.User = append(c.hooks.User, hooks...)
|
||||
}
|
||||
|
||||
// Create returns a create builder for User.
|
||||
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||
// A call to `Intercept(f, g, h)` equals to `user.Intercept(f(g(h())))`.
|
||||
func (c *UserClient) Intercept(interceptors ...Interceptor) {
|
||||
c.inters.User = append(c.inters.User, interceptors...)
|
||||
}
|
||||
|
||||
// Create returns a builder for creating a User entity.
|
||||
func (c *UserClient) Create() *UserCreate {
|
||||
mutation := newUserMutation(c.config, OpCreate)
|
||||
return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
|
|
@ -262,6 +390,21 @@ func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk {
|
|||
return &UserCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *UserClient) MapCreateBulk(slice any, setFunc func(*UserCreate, int)) *UserCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &UserCreateBulk{err: fmt.Errorf("calling to UserClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*UserCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &UserCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for User.
|
||||
func (c *UserClient) Update() *UserUpdate {
|
||||
mutation := newUserMutation(c.config, OpUpdate)
|
||||
|
|
@ -269,8 +412,8 @@ func (c *UserClient) Update() *UserUpdate {
|
|||
}
|
||||
|
||||
// UpdateOne returns an update builder for the given entity.
|
||||
func (c *UserClient) UpdateOne(u *User) *UserUpdateOne {
|
||||
mutation := newUserMutation(c.config, OpUpdateOne, withUser(u))
|
||||
func (c *UserClient) UpdateOne(_m *User) *UserUpdateOne {
|
||||
mutation := newUserMutation(c.config, OpUpdateOne, withUser(_m))
|
||||
return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
|
|
@ -286,12 +429,12 @@ func (c *UserClient) Delete() *UserDelete {
|
|||
return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// DeleteOne returns a delete builder for the given entity.
|
||||
func (c *UserClient) DeleteOne(u *User) *UserDeleteOne {
|
||||
return c.DeleteOneID(u.ID)
|
||||
// DeleteOne returns a builder for deleting the given entity.
|
||||
func (c *UserClient) DeleteOne(_m *User) *UserDeleteOne {
|
||||
return c.DeleteOneID(_m.ID)
|
||||
}
|
||||
|
||||
// DeleteOneID returns a delete builder for the given id.
|
||||
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||
func (c *UserClient) DeleteOneID(id int) *UserDeleteOne {
|
||||
builder := c.Delete().Where(user.ID(id))
|
||||
builder.mutation.id = &id
|
||||
|
|
@ -303,6 +446,8 @@ func (c *UserClient) DeleteOneID(id int) *UserDeleteOne {
|
|||
func (c *UserClient) Query() *UserQuery {
|
||||
return &UserQuery{
|
||||
config: c.config,
|
||||
ctx: &QueryContext{Type: TypeUser},
|
||||
inters: c.Interceptors(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,16 +466,16 @@ func (c *UserClient) GetX(ctx context.Context, id int) *User {
|
|||
}
|
||||
|
||||
// QueryOwner queries the owner edge of a User.
|
||||
func (c *UserClient) QueryOwner(u *User) *PasswordTokenQuery {
|
||||
query := &PasswordTokenQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := u.ID
|
||||
func (c *UserClient) QueryOwner(_m *User) *PasswordTokenQuery {
|
||||
query := (&PasswordTokenClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := _m.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(user.Table, user.FieldID, id),
|
||||
sqlgraph.To(passwordtoken.Table, passwordtoken.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, true, user.OwnerTable, user.OwnerColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(u.driver.Dialect(), step)
|
||||
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
|
|
@ -341,3 +486,33 @@ func (c *UserClient) Hooks() []Hook {
|
|||
hooks := c.hooks.User
|
||||
return append(hooks[:len(hooks):len(hooks)], user.Hooks[:]...)
|
||||
}
|
||||
|
||||
// Interceptors returns the client interceptors.
|
||||
func (c *UserClient) Interceptors() []Interceptor {
|
||||
return c.inters.User
|
||||
}
|
||||
|
||||
func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) {
|
||||
switch m.Op() {
|
||||
case OpCreate:
|
||||
return (&UserCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdate:
|
||||
return (&UserUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdateOne:
|
||||
return (&UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpDelete, OpDeleteOne:
|
||||
return (&UserDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("ent: unknown User mutation op: %q", m.Op())
|
||||
}
|
||||
}
|
||||
|
||||
// hooks and interceptors per client, for fast access.
|
||||
type (
|
||||
hooks struct {
|
||||
PasswordToken, User []ent.Hook
|
||||
}
|
||||
inters struct {
|
||||
PasswordToken, User []ent.Interceptor
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
)
|
||||
|
||||
// Option function to configure the client.
|
||||
type Option func(*config)
|
||||
|
||||
// Config is the configuration for the client and its builder.
|
||||
type config struct {
|
||||
// driver used for executing database requests.
|
||||
driver dialect.Driver
|
||||
// debug enable a debug logging.
|
||||
debug bool
|
||||
// log used for logging on debug mode.
|
||||
log func(...interface{})
|
||||
// hooks to execute on mutations.
|
||||
hooks *hooks
|
||||
}
|
||||
|
||||
// hooks per client, for fast access.
|
||||
type hooks struct {
|
||||
PasswordToken []ent.Hook
|
||||
User []ent.Hook
|
||||
}
|
||||
|
||||
// Options applies the options on the config object.
|
||||
func (c *config) options(opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
if c.debug {
|
||||
c.driver = dialect.Debug(c.driver, c.log)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug enables debug logging on the ent.Driver.
|
||||
func Debug() Option {
|
||||
return func(c *config) {
|
||||
c.debug = true
|
||||
}
|
||||
}
|
||||
|
||||
// Log sets the logging function for debug mode.
|
||||
func Log(fn func(...interface{})) Option {
|
||||
return func(c *config) {
|
||||
c.log = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Driver configures the client driver.
|
||||
func Driver(driver dialect.Driver) Option {
|
||||
return func(c *config) {
|
||||
c.driver = driver
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type clientCtxKey struct{}
|
||||
|
||||
// FromContext returns a Client stored inside a context, or nil if there isn't one.
|
||||
func FromContext(ctx context.Context) *Client {
|
||||
c, _ := ctx.Value(clientCtxKey{}).(*Client)
|
||||
return c
|
||||
}
|
||||
|
||||
// NewContext returns a new context with the given Client attached.
|
||||
func NewContext(parent context.Context, c *Client) context.Context {
|
||||
return context.WithValue(parent, clientCtxKey{}, c)
|
||||
}
|
||||
|
||||
type txCtxKey struct{}
|
||||
|
||||
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
|
||||
func TxFromContext(ctx context.Context) *Tx {
|
||||
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
|
||||
return tx
|
||||
}
|
||||
|
||||
// NewTxContext returns a new context with the given Tx attached.
|
||||
func NewTxContext(parent context.Context, tx *Tx) context.Context {
|
||||
return context.WithValue(parent, txCtxKey{}, tx)
|
||||
}
|
||||
439
ent/ent.go
439
ent/ent.go
|
|
@ -1,58 +1,91 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/user"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// ent aliases to avoid import conflicts in user's code.
|
||||
type (
|
||||
Op = ent.Op
|
||||
Hook = ent.Hook
|
||||
Value = ent.Value
|
||||
Query = ent.Query
|
||||
Policy = ent.Policy
|
||||
Mutator = ent.Mutator
|
||||
Mutation = ent.Mutation
|
||||
MutateFunc = ent.MutateFunc
|
||||
Op = ent.Op
|
||||
Hook = ent.Hook
|
||||
Value = ent.Value
|
||||
Query = ent.Query
|
||||
QueryContext = ent.QueryContext
|
||||
Querier = ent.Querier
|
||||
QuerierFunc = ent.QuerierFunc
|
||||
Interceptor = ent.Interceptor
|
||||
InterceptFunc = ent.InterceptFunc
|
||||
Traverser = ent.Traverser
|
||||
TraverseFunc = ent.TraverseFunc
|
||||
Policy = ent.Policy
|
||||
Mutator = ent.Mutator
|
||||
Mutation = ent.Mutation
|
||||
MutateFunc = ent.MutateFunc
|
||||
)
|
||||
|
||||
type clientCtxKey struct{}
|
||||
|
||||
// FromContext returns a Client stored inside a context, or nil if there isn't one.
|
||||
func FromContext(ctx context.Context) *Client {
|
||||
c, _ := ctx.Value(clientCtxKey{}).(*Client)
|
||||
return c
|
||||
}
|
||||
|
||||
// NewContext returns a new context with the given Client attached.
|
||||
func NewContext(parent context.Context, c *Client) context.Context {
|
||||
return context.WithValue(parent, clientCtxKey{}, c)
|
||||
}
|
||||
|
||||
type txCtxKey struct{}
|
||||
|
||||
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
|
||||
func TxFromContext(ctx context.Context) *Tx {
|
||||
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
|
||||
return tx
|
||||
}
|
||||
|
||||
// NewTxContext returns a new context with the given Tx attached.
|
||||
func NewTxContext(parent context.Context, tx *Tx) context.Context {
|
||||
return context.WithValue(parent, txCtxKey{}, tx)
|
||||
}
|
||||
|
||||
// OrderFunc applies an ordering on the sql selector.
|
||||
// Deprecated: Use Asc/Desc functions or the package builders instead.
|
||||
type OrderFunc func(*sql.Selector)
|
||||
|
||||
// columnChecker returns a function indicates if the column exists in the given column.
|
||||
func columnChecker(table string) func(string) error {
|
||||
checks := map[string]func(string) bool{
|
||||
passwordtoken.Table: passwordtoken.ValidColumn,
|
||||
user.Table: user.ValidColumn,
|
||||
}
|
||||
check, ok := checks[table]
|
||||
if !ok {
|
||||
return func(string) error {
|
||||
return fmt.Errorf("unknown table %q", table)
|
||||
}
|
||||
}
|
||||
return func(column string) error {
|
||||
if !check(column) {
|
||||
return fmt.Errorf("unknown column %q for table %q", column, table)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
initCheck sync.Once
|
||||
columnCheck sql.ColumnCheck
|
||||
)
|
||||
|
||||
// checkColumn checks if the column exists in the given table.
|
||||
func checkColumn(t, c string) error {
|
||||
initCheck.Do(func() {
|
||||
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
|
||||
passwordtoken.Table: passwordtoken.ValidColumn,
|
||||
user.Table: user.ValidColumn,
|
||||
})
|
||||
})
|
||||
return columnCheck(t, c)
|
||||
}
|
||||
|
||||
// Asc applies the given fields in ASC order.
|
||||
func Asc(fields ...string) OrderFunc {
|
||||
func Asc(fields ...string) func(*sql.Selector) {
|
||||
return func(s *sql.Selector) {
|
||||
check := columnChecker(s.TableName())
|
||||
for _, f := range fields {
|
||||
if err := check(f); err != nil {
|
||||
if err := checkColumn(s.TableName(), f); err != nil {
|
||||
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
|
||||
}
|
||||
s.OrderBy(sql.Asc(s.C(f)))
|
||||
|
|
@ -61,11 +94,10 @@ func Asc(fields ...string) OrderFunc {
|
|||
}
|
||||
|
||||
// Desc applies the given fields in DESC order.
|
||||
func Desc(fields ...string) OrderFunc {
|
||||
func Desc(fields ...string) func(*sql.Selector) {
|
||||
return func(s *sql.Selector) {
|
||||
check := columnChecker(s.TableName())
|
||||
for _, f := range fields {
|
||||
if err := check(f); err != nil {
|
||||
if err := checkColumn(s.TableName(), f); err != nil {
|
||||
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
|
||||
}
|
||||
s.OrderBy(sql.Desc(s.C(f)))
|
||||
|
|
@ -81,7 +113,6 @@ type AggregateFunc func(*sql.Selector) string
|
|||
// GroupBy(field1, field2).
|
||||
// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")).
|
||||
// Scan(ctx, &v)
|
||||
//
|
||||
func As(fn AggregateFunc, end string) AggregateFunc {
|
||||
return func(s *sql.Selector) string {
|
||||
return sql.As(fn(s), end)
|
||||
|
|
@ -98,8 +129,7 @@ func Count() AggregateFunc {
|
|||
// Max applies the "max" aggregation function on the given field of each group.
|
||||
func Max(field string) AggregateFunc {
|
||||
return func(s *sql.Selector) string {
|
||||
check := columnChecker(s.TableName())
|
||||
if err := check(field); err != nil {
|
||||
if err := checkColumn(s.TableName(), field); err != nil {
|
||||
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||
return ""
|
||||
}
|
||||
|
|
@ -110,8 +140,7 @@ func Max(field string) AggregateFunc {
|
|||
// Mean applies the "mean" aggregation function on the given field of each group.
|
||||
func Mean(field string) AggregateFunc {
|
||||
return func(s *sql.Selector) string {
|
||||
check := columnChecker(s.TableName())
|
||||
if err := check(field); err != nil {
|
||||
if err := checkColumn(s.TableName(), field); err != nil {
|
||||
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||
return ""
|
||||
}
|
||||
|
|
@ -122,8 +151,7 @@ func Mean(field string) AggregateFunc {
|
|||
// Min applies the "min" aggregation function on the given field of each group.
|
||||
func Min(field string) AggregateFunc {
|
||||
return func(s *sql.Selector) string {
|
||||
check := columnChecker(s.TableName())
|
||||
if err := check(field); err != nil {
|
||||
if err := checkColumn(s.TableName(), field); err != nil {
|
||||
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||
return ""
|
||||
}
|
||||
|
|
@ -134,8 +162,7 @@ func Min(field string) AggregateFunc {
|
|||
// Sum applies the "sum" aggregation function on the given field of each group.
|
||||
func Sum(field string) AggregateFunc {
|
||||
return func(s *sql.Selector) string {
|
||||
check := columnChecker(s.TableName())
|
||||
if err := check(field); err != nil {
|
||||
if err := checkColumn(s.TableName(), field); err != nil {
|
||||
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||
return ""
|
||||
}
|
||||
|
|
@ -143,7 +170,7 @@ func Sum(field string) AggregateFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// ValidationError returns when validating a field fails.
|
||||
// ValidationError returns when validating a field or edge fails.
|
||||
type ValidationError struct {
|
||||
Name string // Field or edge name.
|
||||
err error
|
||||
|
|
@ -259,3 +286,325 @@ func IsConstraintError(err error) bool {
|
|||
var e *ConstraintError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
// selector embedded by the different Select/GroupBy builders.
|
||||
type selector struct {
|
||||
label string
|
||||
flds *[]string
|
||||
fns []AggregateFunc
|
||||
scan func(context.Context, any) error
|
||||
}
|
||||
|
||||
// ScanX is like Scan, but panics if an error occurs.
|
||||
func (s *selector) ScanX(ctx context.Context, v any) {
|
||||
if err := s.scan(ctx, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Strings returns list of strings from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Strings(ctx context.Context) ([]string, error) {
|
||||
if len(*s.flds) > 1 {
|
||||
return nil, errors.New("ent: Strings is not achievable when selecting more than 1 field")
|
||||
}
|
||||
var v []string
|
||||
if err := s.scan(ctx, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// StringsX is like Strings, but panics if an error occurs.
|
||||
func (s *selector) StringsX(ctx context.Context) []string {
|
||||
v, err := s.Strings(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// String returns a single string from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) String(ctx context.Context) (_ string, err error) {
|
||||
var v []string
|
||||
if v, err = s.Strings(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(v) {
|
||||
case 1:
|
||||
return v[0], nil
|
||||
case 0:
|
||||
err = &NotFoundError{s.label}
|
||||
default:
|
||||
err = fmt.Errorf("ent: Strings returned %d results when one was expected", len(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// StringX is like String, but panics if an error occurs.
|
||||
func (s *selector) StringX(ctx context.Context) string {
|
||||
v, err := s.String(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Ints returns list of ints from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Ints(ctx context.Context) ([]int, error) {
|
||||
if len(*s.flds) > 1 {
|
||||
return nil, errors.New("ent: Ints is not achievable when selecting more than 1 field")
|
||||
}
|
||||
var v []int
|
||||
if err := s.scan(ctx, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// IntsX is like Ints, but panics if an error occurs.
|
||||
func (s *selector) IntsX(ctx context.Context) []int {
|
||||
v, err := s.Ints(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Int returns a single int from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Int(ctx context.Context) (_ int, err error) {
|
||||
var v []int
|
||||
if v, err = s.Ints(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(v) {
|
||||
case 1:
|
||||
return v[0], nil
|
||||
case 0:
|
||||
err = &NotFoundError{s.label}
|
||||
default:
|
||||
err = fmt.Errorf("ent: Ints returned %d results when one was expected", len(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IntX is like Int, but panics if an error occurs.
|
||||
func (s *selector) IntX(ctx context.Context) int {
|
||||
v, err := s.Int(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Float64s(ctx context.Context) ([]float64, error) {
|
||||
if len(*s.flds) > 1 {
|
||||
return nil, errors.New("ent: Float64s is not achievable when selecting more than 1 field")
|
||||
}
|
||||
var v []float64
|
||||
if err := s.scan(ctx, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Float64sX is like Float64s, but panics if an error occurs.
|
||||
func (s *selector) Float64sX(ctx context.Context) []float64 {
|
||||
v, err := s.Float64s(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Float64(ctx context.Context) (_ float64, err error) {
|
||||
var v []float64
|
||||
if v, err = s.Float64s(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(v) {
|
||||
case 1:
|
||||
return v[0], nil
|
||||
case 0:
|
||||
err = &NotFoundError{s.label}
|
||||
default:
|
||||
err = fmt.Errorf("ent: Float64s returned %d results when one was expected", len(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Float64X is like Float64, but panics if an error occurs.
|
||||
func (s *selector) Float64X(ctx context.Context) float64 {
|
||||
v, err := s.Float64(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Bools returns list of bools from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Bools(ctx context.Context) ([]bool, error) {
|
||||
if len(*s.flds) > 1 {
|
||||
return nil, errors.New("ent: Bools is not achievable when selecting more than 1 field")
|
||||
}
|
||||
var v []bool
|
||||
if err := s.scan(ctx, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// BoolsX is like Bools, but panics if an error occurs.
|
||||
func (s *selector) BoolsX(ctx context.Context) []bool {
|
||||
v, err := s.Bools(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Bool returns a single bool from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) Bool(ctx context.Context) (_ bool, err error) {
|
||||
var v []bool
|
||||
if v, err = s.Bools(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(v) {
|
||||
case 1:
|
||||
return v[0], nil
|
||||
case 0:
|
||||
err = &NotFoundError{s.label}
|
||||
default:
|
||||
err = fmt.Errorf("ent: Bools returned %d results when one was expected", len(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BoolX is like Bool, but panics if an error occurs.
|
||||
func (s *selector) BoolX(ctx context.Context) bool {
|
||||
v, err := s.Bool(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// withHooks invokes the builder operation with the given hooks, if any.
|
||||
func withHooks[V Value, M any, PM interface {
|
||||
*M
|
||||
Mutation
|
||||
}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) {
|
||||
if len(hooks) == 0 {
|
||||
return exec(ctx)
|
||||
}
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutationT, ok := any(m).(PM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
// Set the mutation to the builder.
|
||||
*mutation = *mutationT
|
||||
return exec(ctx)
|
||||
})
|
||||
for i := len(hooks) - 1; i >= 0; i-- {
|
||||
if hooks[i] == nil {
|
||||
return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = hooks[i](mut)
|
||||
}
|
||||
v, err := mut.Mutate(ctx, mutation)
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
nv, ok := v.(V)
|
||||
if !ok {
|
||||
return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation)
|
||||
}
|
||||
return nv, nil
|
||||
}
|
||||
|
||||
// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist.
|
||||
func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context {
|
||||
if ent.QueryFromContext(ctx) == nil {
|
||||
qc.Op = op
|
||||
ctx = ent.NewQueryContext(ctx, qc)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func querierAll[V Value, Q interface {
|
||||
sqlAll(context.Context, ...queryHook) (V, error)
|
||||
}]() Querier {
|
||||
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
return query.sqlAll(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func querierCount[Q interface {
|
||||
sqlCount(context.Context) (int, error)
|
||||
}]() Querier {
|
||||
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
return query.sqlCount(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) {
|
||||
for i := len(inters) - 1; i >= 0; i-- {
|
||||
qr = inters[i].Intercept(qr)
|
||||
}
|
||||
rv, err := qr.Query(ctx, q)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
vt, ok := rv.(V)
|
||||
if !ok {
|
||||
return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v)
|
||||
}
|
||||
return vt, nil
|
||||
}
|
||||
|
||||
func scanWithInterceptors[Q1 ent.Query, Q2 interface {
|
||||
sqlScan(context.Context, Q1, any) error
|
||||
}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error {
|
||||
rv := reflect.ValueOf(v)
|
||||
var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q1)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
if err := selectOrGroup.sqlScan(ctx, query, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() {
|
||||
return rv.Elem().Interface(), nil
|
||||
}
|
||||
return v, nil
|
||||
})
|
||||
for i := len(inters) - 1; i >= 0; i-- {
|
||||
qr = inters[i].Intercept(qr)
|
||||
}
|
||||
vv, err := qr.Query(ctx, rootQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch rv2 := reflect.ValueOf(vv); {
|
||||
case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer:
|
||||
case rv.Type() == rv2.Type():
|
||||
rv.Elem().Set(rv2.Elem())
|
||||
case rv.Elem().Type() == rv2.Type():
|
||||
rv.Elem().Set(rv2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryHook describes an internal hook for the different sqlAll methods.
|
||||
type queryHook func(context.Context, *sqlgraph.QuerySpec)
|
||||
|
|
|
|||
22
ent/entc.go
Normal file
22
ent/entc.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"entgo.io/ent/entc"
|
||||
"entgo.io/ent/entc/gen"
|
||||
"github.com/mikestefanello/pagoda/ent/admin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := entc.Generate("./schema",
|
||||
&gen.Config{},
|
||||
entc.Extensions(&admin.Extension{}),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("running ent codegen:", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package enttest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"goweb/ent"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
// required by schema hooks.
|
||||
_ "goweb/ent/runtime"
|
||||
_ "github.com/mikestefanello/pagoda/ent/runtime"
|
||||
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
"github.com/mikestefanello/pagoda/ent/migrate"
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -16,7 +18,7 @@ type (
|
|||
// testing.T and testing.B and used by enttest.
|
||||
TestingT interface {
|
||||
FailNow()
|
||||
Error(...interface{})
|
||||
Error(...any)
|
||||
}
|
||||
|
||||
// Option configures client creation.
|
||||
|
|
@ -58,10 +60,7 @@ func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Cl
|
|||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
migrateSchema(t, c, o)
|
||||
return c
|
||||
}
|
||||
|
||||
|
|
@ -69,9 +68,17 @@ func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Cl
|
|||
func NewClient(t TestingT, opts ...Option) *ent.Client {
|
||||
o := newOptions(opts)
|
||||
c := ent.NewClient(o.opts...)
|
||||
if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil {
|
||||
migrateSchema(t, c, o)
|
||||
return c
|
||||
}
|
||||
func migrateSchema(t TestingT, c *ent.Client, o *options) {
|
||||
tables, err := schema.CopyTables(migrate.Tables)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package ent
|
||||
|
||||
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
|
||||
//go:generate go run -mod=mod entc.go
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"goweb/ent"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
)
|
||||
|
||||
// The PasswordTokenFunc type is an adapter to allow the use of ordinary
|
||||
|
|
@ -14,11 +15,10 @@ type PasswordTokenFunc func(context.Context, *ent.PasswordTokenMutation) (ent.Va
|
|||
|
||||
// Mutate calls f(ctx, m).
|
||||
func (f PasswordTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
mv, ok := m.(*ent.PasswordTokenMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PasswordTokenMutation", m)
|
||||
if mv, ok := m.(*ent.PasswordTokenMutation); ok {
|
||||
return f(ctx, mv)
|
||||
}
|
||||
return f(ctx, mv)
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PasswordTokenMutation", m)
|
||||
}
|
||||
|
||||
// The UserFunc type is an adapter to allow the use of ordinary
|
||||
|
|
@ -27,11 +27,10 @@ type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error)
|
|||
|
||||
// Mutate calls f(ctx, m).
|
||||
func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
mv, ok := m.(*ent.UserMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m)
|
||||
if mv, ok := m.(*ent.UserMutation); ok {
|
||||
return f(ctx, mv)
|
||||
}
|
||||
return f(ctx, mv)
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m)
|
||||
}
|
||||
|
||||
// Condition is a hook condition function.
|
||||
|
|
@ -129,7 +128,6 @@ func HasFields(field string, fields ...string) Condition {
|
|||
// If executes the given hook under condition.
|
||||
//
|
||||
// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...)))
|
||||
//
|
||||
func If(hk ent.Hook, cond Condition) ent.Hook {
|
||||
return func(next ent.Mutator) ent.Mutator {
|
||||
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
|
|
@ -144,7 +142,6 @@ func If(hk ent.Hook, cond Condition) ent.Hook {
|
|||
// On executes the given hook only for the given operation.
|
||||
//
|
||||
// hook.On(Log, ent.Delete|ent.Create)
|
||||
//
|
||||
func On(hk ent.Hook, op ent.Op) ent.Hook {
|
||||
return If(hk, HasOp(op))
|
||||
}
|
||||
|
|
@ -152,7 +149,6 @@ func On(hk ent.Hook, op ent.Op) ent.Hook {
|
|||
// Unless skips the given hook only for the given operation.
|
||||
//
|
||||
// hook.Unless(Log, ent.Update|ent.UpdateOne)
|
||||
//
|
||||
func Unless(hk ent.Hook, op ent.Op) ent.Hook {
|
||||
return If(hk, Not(HasOp(op)))
|
||||
}
|
||||
|
|
@ -173,7 +169,6 @@ func FixedError(err error) ent.Hook {
|
|||
// Reject(ent.Delete|ent.Update),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
func Reject(op ent.Op) ent.Hook {
|
||||
hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
|
||||
return On(hk, op)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package migrate
|
||||
|
||||
|
|
@ -28,17 +28,13 @@ var (
|
|||
// and therefore, it's recommended to enable this option to get more
|
||||
// flexibility in the schema changes.
|
||||
WithDropIndex = schema.WithDropIndex
|
||||
// WithFixture sets the foreign-key renaming option to the migration when upgrading
|
||||
// ent from v0.1.0 (issue-#285). Defaults to false.
|
||||
WithFixture = schema.WithFixture
|
||||
// WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true.
|
||||
WithForeignKeys = schema.WithForeignKeys
|
||||
)
|
||||
|
||||
// Schema is the API for creating, migrating and dropping a schema.
|
||||
type Schema struct {
|
||||
drv dialect.Driver
|
||||
universalID bool
|
||||
drv dialect.Driver
|
||||
}
|
||||
|
||||
// NewSchema creates a new schema client.
|
||||
|
|
@ -46,27 +42,23 @@ func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
|
|||
|
||||
// Create creates all schema resources.
|
||||
func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error {
|
||||
return Create(ctx, s, Tables, opts...)
|
||||
}
|
||||
|
||||
// Create creates all table resources using the given schema driver.
|
||||
func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error {
|
||||
migrate, err := schema.NewMigrate(s.drv, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ent/migrate: %w", err)
|
||||
}
|
||||
return migrate.Create(ctx, Tables...)
|
||||
return migrate.Create(ctx, tables...)
|
||||
}
|
||||
|
||||
// WriteTo writes the schema changes to w instead of running them against the database.
|
||||
//
|
||||
// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
|
||||
// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error {
|
||||
drv := &schema.WriteDriver{
|
||||
Writer: w,
|
||||
Driver: s.drv,
|
||||
}
|
||||
migrate, err := schema.NewMigrate(drv, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ent/migrate: %w", err)
|
||||
}
|
||||
return migrate.Create(ctx, Tables...)
|
||||
return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package migrate
|
||||
|
||||
|
|
@ -11,9 +11,9 @@ var (
|
|||
// PasswordTokensColumns holds the columns for the "password_tokens" table.
|
||||
PasswordTokensColumns = []*schema.Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "hash", Type: field.TypeString},
|
||||
{Name: "token", Type: field.TypeString},
|
||||
{Name: "created_at", Type: field.TypeTime},
|
||||
{Name: "password_token_user", Type: field.TypeInt, Nullable: true},
|
||||
{Name: "user_id", Type: field.TypeInt},
|
||||
}
|
||||
// PasswordTokensTable holds the schema information for the "password_tokens" table.
|
||||
PasswordTokensTable = &schema.Table{
|
||||
|
|
@ -25,7 +25,7 @@ var (
|
|||
Symbol: "password_tokens_users_user",
|
||||
Columns: []*schema.Column{PasswordTokensColumns[3]},
|
||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||
OnDelete: schema.SetNull,
|
||||
OnDelete: schema.NoAction,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -35,6 +35,8 @@ var (
|
|||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "email", Type: field.TypeString, Unique: true},
|
||||
{Name: "password", Type: field.TypeString},
|
||||
{Name: "verified", Type: field.TypeBool, Default: false},
|
||||
{Name: "admin", Type: field.TypeBool, Default: false},
|
||||
{Name: "created_at", Type: field.TypeTime},
|
||||
}
|
||||
// UsersTable holds the schema information for the "users" table.
|
||||
|
|
|
|||
344
ent/mutation.go
344
ent/mutation.go
|
|
@ -1,17 +1,19 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/predicate"
|
||||
"goweb/ent/user"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -33,7 +35,7 @@ type PasswordTokenMutation struct {
|
|||
op Op
|
||||
typ string
|
||||
id *int
|
||||
hash *string
|
||||
token *string
|
||||
created_at *time.Time
|
||||
clearedFields map[string]struct{}
|
||||
user *int
|
||||
|
|
@ -73,7 +75,7 @@ func withPasswordTokenID(id int) passwordtokenOption {
|
|||
m.oldValue = func(ctx context.Context) (*PasswordToken, error) {
|
||||
once.Do(func() {
|
||||
if m.done {
|
||||
err = fmt.Errorf("querying old values post mutation is not allowed")
|
||||
err = errors.New("querying old values post mutation is not allowed")
|
||||
} else {
|
||||
value, err = m.Client().PasswordToken.Get(ctx, id)
|
||||
}
|
||||
|
|
@ -106,7 +108,7 @@ func (m PasswordTokenMutation) Client() *Client {
|
|||
// it returns an error otherwise.
|
||||
func (m PasswordTokenMutation) Tx() (*Tx, error) {
|
||||
if _, ok := m.driver.(*txDriver); !ok {
|
||||
return nil, fmt.Errorf("ent: mutation is not running in a transaction")
|
||||
return nil, errors.New("ent: mutation is not running in a transaction")
|
||||
}
|
||||
tx := &Tx{config: m.config}
|
||||
tx.init()
|
||||
|
|
@ -122,40 +124,95 @@ func (m *PasswordTokenMutation) ID() (id int, exists bool) {
|
|||
return *m.id, true
|
||||
}
|
||||
|
||||
// SetHash sets the "hash" field.
|
||||
func (m *PasswordTokenMutation) SetHash(s string) {
|
||||
m.hash = &s
|
||||
// IDs queries the database and returns the entity ids that match the mutation's predicate.
|
||||
// That means, if the mutation is applied within a transaction with an isolation level such
|
||||
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
|
||||
// or updated by the mutation.
|
||||
func (m *PasswordTokenMutation) IDs(ctx context.Context) ([]int, error) {
|
||||
switch {
|
||||
case m.op.Is(OpUpdateOne | OpDeleteOne):
|
||||
id, exists := m.ID()
|
||||
if exists {
|
||||
return []int{id}, nil
|
||||
}
|
||||
fallthrough
|
||||
case m.op.Is(OpUpdate | OpDelete):
|
||||
return m.Client().PasswordToken.Query().Where(m.predicates...).IDs(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns the value of the "hash" field in the mutation.
|
||||
func (m *PasswordTokenMutation) Hash() (r string, exists bool) {
|
||||
v := m.hash
|
||||
// SetToken sets the "token" field.
|
||||
func (m *PasswordTokenMutation) SetToken(s string) {
|
||||
m.token = &s
|
||||
}
|
||||
|
||||
// Token returns the value of the "token" field in the mutation.
|
||||
func (m *PasswordTokenMutation) Token() (r string, exists bool) {
|
||||
v := m.token
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldHash returns the old "hash" field's value of the PasswordToken entity.
|
||||
// OldToken returns the old "token" field's value of the PasswordToken entity.
|
||||
// If the PasswordToken object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PasswordTokenMutation) OldHash(ctx context.Context) (v string, err error) {
|
||||
func (m *PasswordTokenMutation) OldToken(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldHash is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldToken is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldHash requires an ID field in the mutation")
|
||||
return v, errors.New("OldToken requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldHash: %w", err)
|
||||
return v, fmt.Errorf("querying old value for OldToken: %w", err)
|
||||
}
|
||||
return oldValue.Hash, nil
|
||||
return oldValue.Token, nil
|
||||
}
|
||||
|
||||
// ResetHash resets all changes to the "hash" field.
|
||||
func (m *PasswordTokenMutation) ResetHash() {
|
||||
m.hash = nil
|
||||
// ResetToken resets all changes to the "token" field.
|
||||
func (m *PasswordTokenMutation) ResetToken() {
|
||||
m.token = nil
|
||||
}
|
||||
|
||||
// SetUserID sets the "user_id" field.
|
||||
func (m *PasswordTokenMutation) SetUserID(i int) {
|
||||
m.user = &i
|
||||
}
|
||||
|
||||
// UserID returns the value of the "user_id" field in the mutation.
|
||||
func (m *PasswordTokenMutation) UserID() (r int, exists bool) {
|
||||
v := m.user
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldUserID returns the old "user_id" field's value of the PasswordToken entity.
|
||||
// If the PasswordToken object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PasswordTokenMutation) OldUserID(ctx context.Context) (v int, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldUserID is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldUserID requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldUserID: %w", err)
|
||||
}
|
||||
return oldValue.UserID, nil
|
||||
}
|
||||
|
||||
// ResetUserID resets all changes to the "user_id" field.
|
||||
func (m *PasswordTokenMutation) ResetUserID() {
|
||||
m.user = nil
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
|
|
@ -177,10 +234,10 @@ func (m *PasswordTokenMutation) CreatedAt() (r time.Time, exists bool) {
|
|||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PasswordTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldCreatedAt is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldCreatedAt requires an ID field in the mutation")
|
||||
return v, errors.New("OldCreatedAt requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -194,14 +251,10 @@ func (m *PasswordTokenMutation) ResetCreatedAt() {
|
|||
m.created_at = nil
|
||||
}
|
||||
|
||||
// SetUserID sets the "user" edge to the User entity by id.
|
||||
func (m *PasswordTokenMutation) SetUserID(id int) {
|
||||
m.user = &id
|
||||
}
|
||||
|
||||
// ClearUser clears the "user" edge to the User entity.
|
||||
func (m *PasswordTokenMutation) ClearUser() {
|
||||
m.cleareduser = true
|
||||
m.clearedFields[passwordtoken.FieldUserID] = struct{}{}
|
||||
}
|
||||
|
||||
// UserCleared reports if the "user" edge to the User entity was cleared.
|
||||
|
|
@ -209,14 +262,6 @@ func (m *PasswordTokenMutation) UserCleared() bool {
|
|||
return m.cleareduser
|
||||
}
|
||||
|
||||
// UserID returns the "user" edge ID in the mutation.
|
||||
func (m *PasswordTokenMutation) UserID() (id int, exists bool) {
|
||||
if m.user != nil {
|
||||
return *m.user, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UserIDs returns the "user" edge IDs in the mutation.
|
||||
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
|
||||
// UserID instead. It exists only for internal usage by the builders.
|
||||
|
|
@ -238,11 +283,26 @@ func (m *PasswordTokenMutation) Where(ps ...predicate.PasswordToken) {
|
|||
m.predicates = append(m.predicates, ps...)
|
||||
}
|
||||
|
||||
// WhereP appends storage-level predicates to the PasswordTokenMutation builder. Using this method,
|
||||
// users can use type-assertion to append predicates that do not depend on any generated package.
|
||||
func (m *PasswordTokenMutation) WhereP(ps ...func(*sql.Selector)) {
|
||||
p := make([]predicate.PasswordToken, len(ps))
|
||||
for i := range ps {
|
||||
p[i] = ps[i]
|
||||
}
|
||||
m.Where(p...)
|
||||
}
|
||||
|
||||
// Op returns the operation name.
|
||||
func (m *PasswordTokenMutation) Op() Op {
|
||||
return m.op
|
||||
}
|
||||
|
||||
// SetOp allows setting the mutation operation.
|
||||
func (m *PasswordTokenMutation) SetOp(op Op) {
|
||||
m.op = op
|
||||
}
|
||||
|
||||
// Type returns the node type of this mutation (PasswordToken).
|
||||
func (m *PasswordTokenMutation) Type() string {
|
||||
return m.typ
|
||||
|
|
@ -252,9 +312,12 @@ func (m *PasswordTokenMutation) Type() string {
|
|||
// order to get all numeric fields that were incremented/decremented, call
|
||||
// AddedFields().
|
||||
func (m *PasswordTokenMutation) Fields() []string {
|
||||
fields := make([]string, 0, 2)
|
||||
if m.hash != nil {
|
||||
fields = append(fields, passwordtoken.FieldHash)
|
||||
fields := make([]string, 0, 3)
|
||||
if m.token != nil {
|
||||
fields = append(fields, passwordtoken.FieldToken)
|
||||
}
|
||||
if m.user != nil {
|
||||
fields = append(fields, passwordtoken.FieldUserID)
|
||||
}
|
||||
if m.created_at != nil {
|
||||
fields = append(fields, passwordtoken.FieldCreatedAt)
|
||||
|
|
@ -267,8 +330,10 @@ func (m *PasswordTokenMutation) Fields() []string {
|
|||
// schema.
|
||||
func (m *PasswordTokenMutation) Field(name string) (ent.Value, bool) {
|
||||
switch name {
|
||||
case passwordtoken.FieldHash:
|
||||
return m.Hash()
|
||||
case passwordtoken.FieldToken:
|
||||
return m.Token()
|
||||
case passwordtoken.FieldUserID:
|
||||
return m.UserID()
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
return m.CreatedAt()
|
||||
}
|
||||
|
|
@ -280,8 +345,10 @@ func (m *PasswordTokenMutation) Field(name string) (ent.Value, bool) {
|
|||
// database failed.
|
||||
func (m *PasswordTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
|
||||
switch name {
|
||||
case passwordtoken.FieldHash:
|
||||
return m.OldHash(ctx)
|
||||
case passwordtoken.FieldToken:
|
||||
return m.OldToken(ctx)
|
||||
case passwordtoken.FieldUserID:
|
||||
return m.OldUserID(ctx)
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
return m.OldCreatedAt(ctx)
|
||||
}
|
||||
|
|
@ -293,12 +360,19 @@ func (m *PasswordTokenMutation) OldField(ctx context.Context, name string) (ent.
|
|||
// type.
|
||||
func (m *PasswordTokenMutation) SetField(name string, value ent.Value) error {
|
||||
switch name {
|
||||
case passwordtoken.FieldHash:
|
||||
case passwordtoken.FieldToken:
|
||||
v, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetHash(v)
|
||||
m.SetToken(v)
|
||||
return nil
|
||||
case passwordtoken.FieldUserID:
|
||||
v, ok := value.(int)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetUserID(v)
|
||||
return nil
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
v, ok := value.(time.Time)
|
||||
|
|
@ -314,13 +388,16 @@ func (m *PasswordTokenMutation) SetField(name string, value ent.Value) error {
|
|||
// AddedFields returns all numeric fields that were incremented/decremented during
|
||||
// this mutation.
|
||||
func (m *PasswordTokenMutation) AddedFields() []string {
|
||||
return nil
|
||||
var fields []string
|
||||
return fields
|
||||
}
|
||||
|
||||
// AddedField returns the numeric value that was incremented/decremented on a field
|
||||
// with the given name. The second boolean return value indicates that this field
|
||||
// was not set, or was not defined in the schema.
|
||||
func (m *PasswordTokenMutation) AddedField(name string) (ent.Value, bool) {
|
||||
switch name {
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
|
@ -356,8 +433,11 @@ func (m *PasswordTokenMutation) ClearField(name string) error {
|
|||
// It returns an error if the field is not defined in the schema.
|
||||
func (m *PasswordTokenMutation) ResetField(name string) error {
|
||||
switch name {
|
||||
case passwordtoken.FieldHash:
|
||||
m.ResetHash()
|
||||
case passwordtoken.FieldToken:
|
||||
m.ResetToken()
|
||||
return nil
|
||||
case passwordtoken.FieldUserID:
|
||||
m.ResetUserID()
|
||||
return nil
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
m.ResetCreatedAt()
|
||||
|
|
@ -396,8 +476,6 @@ func (m *PasswordTokenMutation) RemovedEdges() []string {
|
|||
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
|
||||
// the given name in this mutation.
|
||||
func (m *PasswordTokenMutation) RemovedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -451,6 +529,8 @@ type UserMutation struct {
|
|||
name *string
|
||||
email *string
|
||||
password *string
|
||||
verified *bool
|
||||
admin *bool
|
||||
created_at *time.Time
|
||||
clearedFields map[string]struct{}
|
||||
owner map[int]struct{}
|
||||
|
|
@ -491,7 +571,7 @@ func withUserID(id int) userOption {
|
|||
m.oldValue = func(ctx context.Context) (*User, error) {
|
||||
once.Do(func() {
|
||||
if m.done {
|
||||
err = fmt.Errorf("querying old values post mutation is not allowed")
|
||||
err = errors.New("querying old values post mutation is not allowed")
|
||||
} else {
|
||||
value, err = m.Client().User.Get(ctx, id)
|
||||
}
|
||||
|
|
@ -524,7 +604,7 @@ func (m UserMutation) Client() *Client {
|
|||
// it returns an error otherwise.
|
||||
func (m UserMutation) Tx() (*Tx, error) {
|
||||
if _, ok := m.driver.(*txDriver); !ok {
|
||||
return nil, fmt.Errorf("ent: mutation is not running in a transaction")
|
||||
return nil, errors.New("ent: mutation is not running in a transaction")
|
||||
}
|
||||
tx := &Tx{config: m.config}
|
||||
tx.init()
|
||||
|
|
@ -540,6 +620,25 @@ func (m *UserMutation) ID() (id int, exists bool) {
|
|||
return *m.id, true
|
||||
}
|
||||
|
||||
// IDs queries the database and returns the entity ids that match the mutation's predicate.
|
||||
// That means, if the mutation is applied within a transaction with an isolation level such
|
||||
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
|
||||
// or updated by the mutation.
|
||||
func (m *UserMutation) IDs(ctx context.Context) ([]int, error) {
|
||||
switch {
|
||||
case m.op.Is(OpUpdateOne | OpDeleteOne):
|
||||
id, exists := m.ID()
|
||||
if exists {
|
||||
return []int{id}, nil
|
||||
}
|
||||
fallthrough
|
||||
case m.op.Is(OpUpdate | OpDelete):
|
||||
return m.Client().User.Query().Where(m.predicates...).IDs(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
|
||||
}
|
||||
}
|
||||
|
||||
// SetName sets the "name" field.
|
||||
func (m *UserMutation) SetName(s string) {
|
||||
m.name = &s
|
||||
|
|
@ -559,10 +658,10 @@ func (m *UserMutation) Name() (r string, exists bool) {
|
|||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldName(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldName is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldName is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldName requires an ID field in the mutation")
|
||||
return v, errors.New("OldName requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -595,10 +694,10 @@ func (m *UserMutation) Email() (r string, exists bool) {
|
|||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldEmail(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldEmail is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldEmail is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldEmail requires an ID field in the mutation")
|
||||
return v, errors.New("OldEmail requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -631,10 +730,10 @@ func (m *UserMutation) Password() (r string, exists bool) {
|
|||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldPassword(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldPassword is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldPassword is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldPassword requires an ID field in the mutation")
|
||||
return v, errors.New("OldPassword requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -648,6 +747,78 @@ func (m *UserMutation) ResetPassword() {
|
|||
m.password = nil
|
||||
}
|
||||
|
||||
// SetVerified sets the "verified" field.
|
||||
func (m *UserMutation) SetVerified(b bool) {
|
||||
m.verified = &b
|
||||
}
|
||||
|
||||
// Verified returns the value of the "verified" field in the mutation.
|
||||
func (m *UserMutation) Verified() (r bool, exists bool) {
|
||||
v := m.verified
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldVerified returns the old "verified" field's value of the User entity.
|
||||
// If the User object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldVerified(ctx context.Context) (v bool, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldVerified is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldVerified requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldVerified: %w", err)
|
||||
}
|
||||
return oldValue.Verified, nil
|
||||
}
|
||||
|
||||
// ResetVerified resets all changes to the "verified" field.
|
||||
func (m *UserMutation) ResetVerified() {
|
||||
m.verified = nil
|
||||
}
|
||||
|
||||
// SetAdmin sets the "admin" field.
|
||||
func (m *UserMutation) SetAdmin(b bool) {
|
||||
m.admin = &b
|
||||
}
|
||||
|
||||
// Admin returns the value of the "admin" field in the mutation.
|
||||
func (m *UserMutation) Admin() (r bool, exists bool) {
|
||||
v := m.admin
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldAdmin returns the old "admin" field's value of the User entity.
|
||||
// If the User object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldAdmin(ctx context.Context) (v bool, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldAdmin is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldAdmin requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldAdmin: %w", err)
|
||||
}
|
||||
return oldValue.Admin, nil
|
||||
}
|
||||
|
||||
// ResetAdmin resets all changes to the "admin" field.
|
||||
func (m *UserMutation) ResetAdmin() {
|
||||
m.admin = nil
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
func (m *UserMutation) SetCreatedAt(t time.Time) {
|
||||
m.created_at = &t
|
||||
|
|
@ -667,10 +838,10 @@ func (m *UserMutation) CreatedAt() (r time.Time, exists bool) {
|
|||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *UserMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, fmt.Errorf("OldCreatedAt is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, fmt.Errorf("OldCreatedAt requires an ID field in the mutation")
|
||||
return v, errors.New("OldCreatedAt requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -743,11 +914,26 @@ func (m *UserMutation) Where(ps ...predicate.User) {
|
|||
m.predicates = append(m.predicates, ps...)
|
||||
}
|
||||
|
||||
// WhereP appends storage-level predicates to the UserMutation builder. Using this method,
|
||||
// users can use type-assertion to append predicates that do not depend on any generated package.
|
||||
func (m *UserMutation) WhereP(ps ...func(*sql.Selector)) {
|
||||
p := make([]predicate.User, len(ps))
|
||||
for i := range ps {
|
||||
p[i] = ps[i]
|
||||
}
|
||||
m.Where(p...)
|
||||
}
|
||||
|
||||
// Op returns the operation name.
|
||||
func (m *UserMutation) Op() Op {
|
||||
return m.op
|
||||
}
|
||||
|
||||
// SetOp allows setting the mutation operation.
|
||||
func (m *UserMutation) SetOp(op Op) {
|
||||
m.op = op
|
||||
}
|
||||
|
||||
// Type returns the node type of this mutation (User).
|
||||
func (m *UserMutation) Type() string {
|
||||
return m.typ
|
||||
|
|
@ -757,7 +943,7 @@ func (m *UserMutation) Type() string {
|
|||
// order to get all numeric fields that were incremented/decremented, call
|
||||
// AddedFields().
|
||||
func (m *UserMutation) Fields() []string {
|
||||
fields := make([]string, 0, 4)
|
||||
fields := make([]string, 0, 6)
|
||||
if m.name != nil {
|
||||
fields = append(fields, user.FieldName)
|
||||
}
|
||||
|
|
@ -767,6 +953,12 @@ func (m *UserMutation) Fields() []string {
|
|||
if m.password != nil {
|
||||
fields = append(fields, user.FieldPassword)
|
||||
}
|
||||
if m.verified != nil {
|
||||
fields = append(fields, user.FieldVerified)
|
||||
}
|
||||
if m.admin != nil {
|
||||
fields = append(fields, user.FieldAdmin)
|
||||
}
|
||||
if m.created_at != nil {
|
||||
fields = append(fields, user.FieldCreatedAt)
|
||||
}
|
||||
|
|
@ -784,6 +976,10 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) {
|
|||
return m.Email()
|
||||
case user.FieldPassword:
|
||||
return m.Password()
|
||||
case user.FieldVerified:
|
||||
return m.Verified()
|
||||
case user.FieldAdmin:
|
||||
return m.Admin()
|
||||
case user.FieldCreatedAt:
|
||||
return m.CreatedAt()
|
||||
}
|
||||
|
|
@ -801,6 +997,10 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er
|
|||
return m.OldEmail(ctx)
|
||||
case user.FieldPassword:
|
||||
return m.OldPassword(ctx)
|
||||
case user.FieldVerified:
|
||||
return m.OldVerified(ctx)
|
||||
case user.FieldAdmin:
|
||||
return m.OldAdmin(ctx)
|
||||
case user.FieldCreatedAt:
|
||||
return m.OldCreatedAt(ctx)
|
||||
}
|
||||
|
|
@ -833,6 +1033,20 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
|
|||
}
|
||||
m.SetPassword(v)
|
||||
return nil
|
||||
case user.FieldVerified:
|
||||
v, ok := value.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetVerified(v)
|
||||
return nil
|
||||
case user.FieldAdmin:
|
||||
v, ok := value.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetAdmin(v)
|
||||
return nil
|
||||
case user.FieldCreatedAt:
|
||||
v, ok := value.(time.Time)
|
||||
if !ok {
|
||||
|
|
@ -898,6 +1112,12 @@ func (m *UserMutation) ResetField(name string) error {
|
|||
case user.FieldPassword:
|
||||
m.ResetPassword()
|
||||
return nil
|
||||
case user.FieldVerified:
|
||||
m.ResetVerified()
|
||||
return nil
|
||||
case user.FieldAdmin:
|
||||
m.ResetAdmin()
|
||||
return nil
|
||||
case user.FieldCreatedAt:
|
||||
m.ResetCreatedAt()
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/user"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// PasswordToken is the model entity for the PasswordToken schema.
|
||||
|
|
@ -17,14 +18,16 @@ type PasswordToken struct {
|
|||
config `json:"-"`
|
||||
// ID of the ent.
|
||||
ID int `json:"id,omitempty"`
|
||||
// Hash holds the value of the "hash" field.
|
||||
Hash string `json:"-"`
|
||||
// Token holds the value of the "token" field.
|
||||
Token string `json:"-"`
|
||||
// UserID holds the value of the "user_id" field.
|
||||
UserID int `json:"user_id,omitempty"`
|
||||
// CreatedAt holds the value of the "created_at" field.
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the PasswordTokenQuery when eager-loading is set.
|
||||
Edges PasswordTokenEdges `json:"edges"`
|
||||
password_token_user *int
|
||||
Edges PasswordTokenEdges `json:"edges"`
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// PasswordTokenEdges holds the relations/edges for other nodes in the graph.
|
||||
|
|
@ -39,32 +42,27 @@ type PasswordTokenEdges struct {
|
|||
// UserOrErr returns the User value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e PasswordTokenEdges) UserOrErr() (*User, error) {
|
||||
if e.loadedTypes[0] {
|
||||
if e.User == nil {
|
||||
// The edge user was loaded in eager-loading,
|
||||
// but was not found.
|
||||
return nil, &NotFoundError{label: user.Label}
|
||||
}
|
||||
if e.User != nil {
|
||||
return e.User, nil
|
||||
} else if e.loadedTypes[0] {
|
||||
return nil, &NotFoundError{label: user.Label}
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "user"}
|
||||
}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*PasswordToken) scanValues(columns []string) ([]interface{}, error) {
|
||||
values := make([]interface{}, len(columns))
|
||||
func (*PasswordToken) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case passwordtoken.FieldID:
|
||||
case passwordtoken.FieldID, passwordtoken.FieldUserID:
|
||||
values[i] = new(sql.NullInt64)
|
||||
case passwordtoken.FieldHash:
|
||||
case passwordtoken.FieldToken:
|
||||
values[i] = new(sql.NullString)
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
values[i] = new(sql.NullTime)
|
||||
case passwordtoken.ForeignKeys[0]: // password_token_user
|
||||
values[i] = new(sql.NullInt64)
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type PasswordToken", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
|
|
@ -72,7 +70,7 @@ func (*PasswordToken) scanValues(columns []string) ([]interface{}, error) {
|
|||
|
||||
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||
// to the PasswordToken fields.
|
||||
func (pt *PasswordToken) assignValues(columns []string, values []interface{}) error {
|
||||
func (_m *PasswordToken) assignValues(columns []string, values []any) error {
|
||||
if m, n := len(values), len(columns); m < n {
|
||||
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||
}
|
||||
|
|
@ -83,71 +81,76 @@ func (pt *PasswordToken) assignValues(columns []string, values []interface{}) er
|
|||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field id", value)
|
||||
}
|
||||
pt.ID = int(value.Int64)
|
||||
case passwordtoken.FieldHash:
|
||||
_m.ID = int(value.Int64)
|
||||
case passwordtoken.FieldToken:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field hash", values[i])
|
||||
return fmt.Errorf("unexpected type %T for field token", values[i])
|
||||
} else if value.Valid {
|
||||
pt.Hash = value.String
|
||||
_m.Token = value.String
|
||||
}
|
||||
case passwordtoken.FieldUserID:
|
||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field user_id", values[i])
|
||||
} else if value.Valid {
|
||||
_m.UserID = int(value.Int64)
|
||||
}
|
||||
case passwordtoken.FieldCreatedAt:
|
||||
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||
} else if value.Valid {
|
||||
pt.CreatedAt = value.Time
|
||||
}
|
||||
case passwordtoken.ForeignKeys[0]:
|
||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||
return fmt.Errorf("unexpected type %T for edge-field password_token_user", value)
|
||||
} else if value.Valid {
|
||||
pt.password_token_user = new(int)
|
||||
*pt.password_token_user = int(value.Int64)
|
||||
_m.CreatedAt = value.Time
|
||||
}
|
||||
default:
|
||||
_m.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the PasswordToken.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (_m *PasswordToken) Value(name string) (ent.Value, error) {
|
||||
return _m.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryUser queries the "user" edge of the PasswordToken entity.
|
||||
func (pt *PasswordToken) QueryUser() *UserQuery {
|
||||
return (&PasswordTokenClient{config: pt.config}).QueryUser(pt)
|
||||
func (_m *PasswordToken) QueryUser() *UserQuery {
|
||||
return NewPasswordTokenClient(_m.config).QueryUser(_m)
|
||||
}
|
||||
|
||||
// Update returns a builder for updating this PasswordToken.
|
||||
// Note that you need to call PasswordToken.Unwrap() before calling this method if this PasswordToken
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
func (pt *PasswordToken) Update() *PasswordTokenUpdateOne {
|
||||
return (&PasswordTokenClient{config: pt.config}).UpdateOne(pt)
|
||||
func (_m *PasswordToken) Update() *PasswordTokenUpdateOne {
|
||||
return NewPasswordTokenClient(_m.config).UpdateOne(_m)
|
||||
}
|
||||
|
||||
// Unwrap unwraps the PasswordToken entity that was returned from a transaction after it was closed,
|
||||
// so that all future queries will be executed through the driver which created the transaction.
|
||||
func (pt *PasswordToken) Unwrap() *PasswordToken {
|
||||
tx, ok := pt.config.driver.(*txDriver)
|
||||
func (_m *PasswordToken) Unwrap() *PasswordToken {
|
||||
_tx, ok := _m.config.driver.(*txDriver)
|
||||
if !ok {
|
||||
panic("ent: PasswordToken is not a transactional entity")
|
||||
}
|
||||
pt.config.driver = tx.drv
|
||||
return pt
|
||||
_m.config.driver = _tx.drv
|
||||
return _m
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer.
|
||||
func (pt *PasswordToken) String() string {
|
||||
func (_m *PasswordToken) String() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("PasswordToken(")
|
||||
builder.WriteString(fmt.Sprintf("id=%v", pt.ID))
|
||||
builder.WriteString(", hash=<sensitive>")
|
||||
builder.WriteString(", created_at=")
|
||||
builder.WriteString(pt.CreatedAt.Format(time.ANSIC))
|
||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||
builder.WriteString("token=<sensitive>")
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("user_id=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.UserID))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("created_at=")
|
||||
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// PasswordTokens is a parsable slice of PasswordToken.
|
||||
type PasswordTokens []*PasswordToken
|
||||
|
||||
func (pt PasswordTokens) config(cfg config) {
|
||||
for _i := range pt {
|
||||
pt[_i].config = cfg
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package passwordtoken
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -11,8 +15,10 @@ const (
|
|||
Label = "password_token"
|
||||
// FieldID holds the string denoting the id field in the database.
|
||||
FieldID = "id"
|
||||
// FieldHash holds the string denoting the hash field in the database.
|
||||
FieldHash = "hash"
|
||||
// FieldToken holds the string denoting the token field in the database.
|
||||
FieldToken = "token"
|
||||
// FieldUserID holds the string denoting the user_id field in the database.
|
||||
FieldUserID = "user_id"
|
||||
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
||||
FieldCreatedAt = "created_at"
|
||||
// EdgeUser holds the string denoting the user edge name in mutations.
|
||||
|
|
@ -25,22 +31,17 @@ const (
|
|||
// It exists in this package in order to avoid circular dependency with the "user" package.
|
||||
UserInverseTable = "users"
|
||||
// UserColumn is the table column denoting the user relation/edge.
|
||||
UserColumn = "password_token_user"
|
||||
UserColumn = "user_id"
|
||||
)
|
||||
|
||||
// Columns holds all SQL columns for passwordtoken fields.
|
||||
var Columns = []string{
|
||||
FieldID,
|
||||
FieldHash,
|
||||
FieldToken,
|
||||
FieldUserID,
|
||||
FieldCreatedAt,
|
||||
}
|
||||
|
||||
// ForeignKeys holds the SQL foreign-keys that are owned by the "password_tokens"
|
||||
// table and are not defined as standalone fields in the schema.
|
||||
var ForeignKeys = []string{
|
||||
"password_token_user",
|
||||
}
|
||||
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
func ValidColumn(column string) bool {
|
||||
for i := range Columns {
|
||||
|
|
@ -48,17 +49,55 @@ func ValidColumn(column string) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
for i := range ForeignKeys {
|
||||
if column == ForeignKeys[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Note that the variables below are initialized by the runtime
|
||||
// package on the initialization of the application. Therefore,
|
||||
// it should be imported in the main as follows:
|
||||
//
|
||||
// import _ "github.com/mikestefanello/pagoda/ent/runtime"
|
||||
var (
|
||||
// HashValidator is a validator for the "hash" field. It is called by the builders before save.
|
||||
HashValidator func(string) error
|
||||
Hooks [1]ent.Hook
|
||||
// TokenValidator is a validator for the "token" field. It is called by the builders before save.
|
||||
TokenValidator func(string) error
|
||||
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
||||
DefaultCreatedAt func() time.Time
|
||||
)
|
||||
|
||||
// OrderOption defines the ordering options for the PasswordToken queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByToken orders the results by the token field.
|
||||
func ByToken(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldToken, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUserID orders the results by the user_id field.
|
||||
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUserID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUserField orders the results by user field.
|
||||
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
func newUserStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UserInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,297 +1,198 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package passwordtoken
|
||||
|
||||
import (
|
||||
"goweb/ent/predicate"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
func ID(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDEQ applies the EQ predicate on the ID field.
|
||||
func IDEQ(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDNEQ applies the NEQ predicate on the ID field.
|
||||
func IDNEQ(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldNEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDIn applies the In predicate on the ID field.
|
||||
func IDIn(ids ...int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(ids) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
v := make([]interface{}, len(ids))
|
||||
for i := range v {
|
||||
v[i] = ids[i]
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldID), v...))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDNotIn applies the NotIn predicate on the ID field.
|
||||
func IDNotIn(ids ...int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(ids) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
v := make([]interface{}, len(ids))
|
||||
for i := range v {
|
||||
v[i] = ids[i]
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldID), v...))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldNotIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDGT applies the GT predicate on the ID field.
|
||||
func IDGT(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldGT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDGTE applies the GTE predicate on the ID field.
|
||||
func IDGTE(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldGTE(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLT applies the LT predicate on the ID field.
|
||||
func IDLT(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldLT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLTE applies the LTE predicate on the ID field.
|
||||
func IDLTE(id int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldLTE(FieldID, id))
|
||||
}
|
||||
|
||||
// Hash applies equality check predicate on the "hash" field. It's identical to HashEQ.
|
||||
func Hash(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldHash), v))
|
||||
})
|
||||
// Token applies equality check predicate on the "token" field. It's identical to TokenEQ.
|
||||
func Token(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldToken, v))
|
||||
}
|
||||
|
||||
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
|
||||
func UserID(v int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldUserID, v))
|
||||
}
|
||||
|
||||
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
|
||||
func CreatedAt(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// HashEQ applies the EQ predicate on the "hash" field.
|
||||
func HashEQ(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenEQ applies the EQ predicate on the "token" field.
|
||||
func TokenEQ(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashNEQ applies the NEQ predicate on the "hash" field.
|
||||
func HashNEQ(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenNEQ applies the NEQ predicate on the "token" field.
|
||||
func TokenNEQ(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldNEQ(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashIn applies the In predicate on the "hash" field.
|
||||
func HashIn(vs ...string) predicate.PasswordToken {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldHash), v...))
|
||||
})
|
||||
// TokenIn applies the In predicate on the "token" field.
|
||||
func TokenIn(vs ...string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldIn(FieldToken, vs...))
|
||||
}
|
||||
|
||||
// HashNotIn applies the NotIn predicate on the "hash" field.
|
||||
func HashNotIn(vs ...string) predicate.PasswordToken {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldHash), v...))
|
||||
})
|
||||
// TokenNotIn applies the NotIn predicate on the "token" field.
|
||||
func TokenNotIn(vs ...string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldNotIn(FieldToken, vs...))
|
||||
}
|
||||
|
||||
// HashGT applies the GT predicate on the "hash" field.
|
||||
func HashGT(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenGT applies the GT predicate on the "token" field.
|
||||
func TokenGT(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldGT(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashGTE applies the GTE predicate on the "hash" field.
|
||||
func HashGTE(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenGTE applies the GTE predicate on the "token" field.
|
||||
func TokenGTE(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldGTE(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashLT applies the LT predicate on the "hash" field.
|
||||
func HashLT(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenLT applies the LT predicate on the "token" field.
|
||||
func TokenLT(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldLT(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashLTE applies the LTE predicate on the "hash" field.
|
||||
func HashLTE(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenLTE applies the LTE predicate on the "token" field.
|
||||
func TokenLTE(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldLTE(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashContains applies the Contains predicate on the "hash" field.
|
||||
func HashContains(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.Contains(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenContains applies the Contains predicate on the "token" field.
|
||||
func TokenContains(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldContains(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashHasPrefix applies the HasPrefix predicate on the "hash" field.
|
||||
func HashHasPrefix(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.HasPrefix(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenHasPrefix applies the HasPrefix predicate on the "token" field.
|
||||
func TokenHasPrefix(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldHasPrefix(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashHasSuffix applies the HasSuffix predicate on the "hash" field.
|
||||
func HashHasSuffix(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.HasSuffix(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenHasSuffix applies the HasSuffix predicate on the "token" field.
|
||||
func TokenHasSuffix(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldHasSuffix(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashEqualFold applies the EqualFold predicate on the "hash" field.
|
||||
func HashEqualFold(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EqualFold(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenEqualFold applies the EqualFold predicate on the "token" field.
|
||||
func TokenEqualFold(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldEqualFold(FieldToken, v))
|
||||
}
|
||||
|
||||
// HashContainsFold applies the ContainsFold predicate on the "hash" field.
|
||||
func HashContainsFold(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.ContainsFold(s.C(FieldHash), v))
|
||||
})
|
||||
// TokenContainsFold applies the ContainsFold predicate on the "token" field.
|
||||
func TokenContainsFold(v string) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldContainsFold(FieldToken, v))
|
||||
}
|
||||
|
||||
// UserIDEQ applies the EQ predicate on the "user_id" field.
|
||||
func UserIDEQ(v int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldUserID, v))
|
||||
}
|
||||
|
||||
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
|
||||
func UserIDNEQ(v int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldNEQ(FieldUserID, v))
|
||||
}
|
||||
|
||||
// UserIDIn applies the In predicate on the "user_id" field.
|
||||
func UserIDIn(vs ...int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldIn(FieldUserID, vs...))
|
||||
}
|
||||
|
||||
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
|
||||
func UserIDNotIn(vs ...int) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(sql.FieldNotIn(FieldUserID, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
|
||||
func CreatedAtNEQ(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldNEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtIn applies the In predicate on the "created_at" field.
|
||||
func CreatedAtIn(vs ...time.Time) predicate.PasswordToken {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldCreatedAt), v...))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
|
||||
func CreatedAtNotIn(vs ...time.Time) predicate.PasswordToken {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldNotIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtGT applies the GT predicate on the "created_at" field.
|
||||
func CreatedAtGT(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldGT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
|
||||
func CreatedAtGTE(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldGTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLT applies the LT predicate on the "created_at" field.
|
||||
func CreatedAtLT(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldLT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
|
||||
func CreatedAtLTE(v time.Time) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.PasswordToken(sql.FieldLTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// HasUser applies the HasEdge predicate on the "user" edge.
|
||||
|
|
@ -299,7 +200,6 @@ func HasUser() predicate.PasswordToken {
|
|||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UserTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
|
|
@ -309,11 +209,7 @@ func HasUser() predicate.PasswordToken {
|
|||
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
|
||||
func HasUserWith(preds ...predicate.User) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UserInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
|
||||
)
|
||||
step := newUserStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
|
|
@ -324,32 +220,15 @@ func HasUserWith(preds ...predicate.User) predicate.PasswordToken {
|
|||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.PasswordToken) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for _, p := range predicates {
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.PasswordToken(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.PasswordToken) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for i, p := range predicates {
|
||||
if i > 0 {
|
||||
s1.Or()
|
||||
}
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.PasswordToken(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.PasswordToken) predicate.PasswordToken {
|
||||
return predicate.PasswordToken(func(s *sql.Selector) {
|
||||
p(s.Not())
|
||||
})
|
||||
return predicate.PasswordToken(sql.NotPredicates(p))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
|
|
@ -6,12 +6,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/user"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// PasswordTokenCreate is the builder for creating a PasswordToken entity.
|
||||
|
|
@ -21,87 +21,53 @@ type PasswordTokenCreate struct {
|
|||
hooks []Hook
|
||||
}
|
||||
|
||||
// SetHash sets the "hash" field.
|
||||
func (ptc *PasswordTokenCreate) SetHash(s string) *PasswordTokenCreate {
|
||||
ptc.mutation.SetHash(s)
|
||||
return ptc
|
||||
// SetToken sets the "token" field.
|
||||
func (_c *PasswordTokenCreate) SetToken(v string) *PasswordTokenCreate {
|
||||
_c.mutation.SetToken(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetUserID sets the "user_id" field.
|
||||
func (_c *PasswordTokenCreate) SetUserID(v int) *PasswordTokenCreate {
|
||||
_c.mutation.SetUserID(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
func (ptc *PasswordTokenCreate) SetCreatedAt(t time.Time) *PasswordTokenCreate {
|
||||
ptc.mutation.SetCreatedAt(t)
|
||||
return ptc
|
||||
func (_c *PasswordTokenCreate) SetCreatedAt(v time.Time) *PasswordTokenCreate {
|
||||
_c.mutation.SetCreatedAt(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||
func (ptc *PasswordTokenCreate) SetNillableCreatedAt(t *time.Time) *PasswordTokenCreate {
|
||||
if t != nil {
|
||||
ptc.SetCreatedAt(*t)
|
||||
func (_c *PasswordTokenCreate) SetNillableCreatedAt(v *time.Time) *PasswordTokenCreate {
|
||||
if v != nil {
|
||||
_c.SetCreatedAt(*v)
|
||||
}
|
||||
return ptc
|
||||
}
|
||||
|
||||
// SetUserID sets the "user" edge to the User entity by ID.
|
||||
func (ptc *PasswordTokenCreate) SetUserID(id int) *PasswordTokenCreate {
|
||||
ptc.mutation.SetUserID(id)
|
||||
return ptc
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetUser sets the "user" edge to the User entity.
|
||||
func (ptc *PasswordTokenCreate) SetUser(u *User) *PasswordTokenCreate {
|
||||
return ptc.SetUserID(u.ID)
|
||||
func (_c *PasswordTokenCreate) SetUser(v *User) *PasswordTokenCreate {
|
||||
return _c.SetUserID(v.ID)
|
||||
}
|
||||
|
||||
// Mutation returns the PasswordTokenMutation object of the builder.
|
||||
func (ptc *PasswordTokenCreate) Mutation() *PasswordTokenMutation {
|
||||
return ptc.mutation
|
||||
func (_c *PasswordTokenCreate) Mutation() *PasswordTokenMutation {
|
||||
return _c.mutation
|
||||
}
|
||||
|
||||
// Save creates the PasswordToken in the database.
|
||||
func (ptc *PasswordTokenCreate) Save(ctx context.Context) (*PasswordToken, error) {
|
||||
var (
|
||||
err error
|
||||
node *PasswordToken
|
||||
)
|
||||
ptc.defaults()
|
||||
if len(ptc.hooks) == 0 {
|
||||
if err = ptc.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err = ptc.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*PasswordTokenMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = ptc.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptc.mutation = mutation
|
||||
if node, err = ptc.sqlSave(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mutation.id = &node.ID
|
||||
mutation.done = true
|
||||
return node, err
|
||||
})
|
||||
for i := len(ptc.hooks) - 1; i >= 0; i-- {
|
||||
if ptc.hooks[i] == nil {
|
||||
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = ptc.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, ptc.mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (_c *PasswordTokenCreate) Save(ctx context.Context) (*PasswordToken, error) {
|
||||
if err := _c.defaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, err
|
||||
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
func (ptc *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken {
|
||||
v, err := ptc.Save(ctx)
|
||||
func (_c *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken {
|
||||
v, err := _c.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -109,86 +75,84 @@ func (ptc *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken {
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (ptc *PasswordTokenCreate) Exec(ctx context.Context) error {
|
||||
_, err := ptc.Save(ctx)
|
||||
func (_c *PasswordTokenCreate) Exec(ctx context.Context) error {
|
||||
_, err := _c.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptc *PasswordTokenCreate) ExecX(ctx context.Context) {
|
||||
if err := ptc.Exec(ctx); err != nil {
|
||||
func (_c *PasswordTokenCreate) ExecX(ctx context.Context) {
|
||||
if err := _c.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// defaults sets the default values of the builder before save.
|
||||
func (ptc *PasswordTokenCreate) defaults() {
|
||||
if _, ok := ptc.mutation.CreatedAt(); !ok {
|
||||
v := passwordtoken.DefaultCreatedAt()
|
||||
ptc.mutation.SetCreatedAt(v)
|
||||
}
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (ptc *PasswordTokenCreate) check() error {
|
||||
if _, ok := ptc.mutation.Hash(); !ok {
|
||||
return &ValidationError{Name: "hash", err: errors.New(`ent: missing required field "hash"`)}
|
||||
}
|
||||
if v, ok := ptc.mutation.Hash(); ok {
|
||||
if err := passwordtoken.HashValidator(v); err != nil {
|
||||
return &ValidationError{Name: "hash", err: fmt.Errorf(`ent: validator failed for field "hash": %w`, err)}
|
||||
func (_c *PasswordTokenCreate) defaults() error {
|
||||
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||
if passwordtoken.DefaultCreatedAt == nil {
|
||||
return fmt.Errorf("ent: uninitialized passwordtoken.DefaultCreatedAt (forgotten import ent/runtime?)")
|
||||
}
|
||||
}
|
||||
if _, ok := ptc.mutation.CreatedAt(); !ok {
|
||||
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)}
|
||||
}
|
||||
if _, ok := ptc.mutation.UserID(); !ok {
|
||||
return &ValidationError{Name: "user", err: errors.New("ent: missing required edge \"user\"")}
|
||||
v := passwordtoken.DefaultCreatedAt()
|
||||
_c.mutation.SetCreatedAt(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ptc *PasswordTokenCreate) sqlSave(ctx context.Context) (*PasswordToken, error) {
|
||||
_node, _spec := ptc.createSpec()
|
||||
if err := sqlgraph.CreateNode(ctx, ptc.driver, _spec); err != nil {
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (_c *PasswordTokenCreate) check() error {
|
||||
if _, ok := _c.mutation.Token(); !ok {
|
||||
return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "PasswordToken.token"`)}
|
||||
}
|
||||
if v, ok := _c.mutation.Token(); ok {
|
||||
if err := passwordtoken.TokenValidator(v); err != nil {
|
||||
return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := _c.mutation.UserID(); !ok {
|
||||
return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "PasswordToken.user_id"`)}
|
||||
}
|
||||
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "PasswordToken.created_at"`)}
|
||||
}
|
||||
if len(_c.mutation.UserIDs()) == 0 {
|
||||
return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "PasswordToken.user"`)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_c *PasswordTokenCreate) sqlSave(ctx context.Context) (*PasswordToken, error) {
|
||||
if err := _c.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_node, _spec := _c.createSpec()
|
||||
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
id := _spec.ID.Value.(int64)
|
||||
_node.ID = int(id)
|
||||
_c.mutation.id = &_node.ID
|
||||
_c.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSpec) {
|
||||
func (_c *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSpec) {
|
||||
var (
|
||||
_node = &PasswordToken{config: ptc.config}
|
||||
_spec = &sqlgraph.CreateSpec{
|
||||
Table: passwordtoken.Table,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
}
|
||||
_node = &PasswordToken{config: _c.config}
|
||||
_spec = sqlgraph.NewCreateSpec(passwordtoken.Table, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
|
||||
)
|
||||
if value, ok := ptc.mutation.Hash(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldHash,
|
||||
})
|
||||
_node.Hash = value
|
||||
if value, ok := _c.mutation.Token(); ok {
|
||||
_spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
|
||||
_node.Token = value
|
||||
}
|
||||
if value, ok := ptc.mutation.CreatedAt(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeTime,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldCreatedAt,
|
||||
})
|
||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||
_spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
|
||||
_node.CreatedAt = value
|
||||
}
|
||||
if nodes := ptc.mutation.UserIDs(); len(nodes) > 0 {
|
||||
if nodes := _c.mutation.UserIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
|
|
@ -196,16 +160,13 @@ func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSp
|
|||
Columns: []string{passwordtoken.UserColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_node.password_token_user = &nodes[0]
|
||||
_node.UserID = nodes[0]
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
return _node, _spec
|
||||
|
|
@ -214,17 +175,21 @@ func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSp
|
|||
// PasswordTokenCreateBulk is the builder for creating many PasswordToken entities in bulk.
|
||||
type PasswordTokenCreateBulk struct {
|
||||
config
|
||||
err error
|
||||
builders []*PasswordTokenCreate
|
||||
}
|
||||
|
||||
// Save creates the PasswordToken entities in the database.
|
||||
func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken, error) {
|
||||
specs := make([]*sqlgraph.CreateSpec, len(ptcb.builders))
|
||||
nodes := make([]*PasswordToken, len(ptcb.builders))
|
||||
mutators := make([]Mutator, len(ptcb.builders))
|
||||
for i := range ptcb.builders {
|
||||
func (_c *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken, error) {
|
||||
if _c.err != nil {
|
||||
return nil, _c.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
|
||||
nodes := make([]*PasswordToken, len(_c.builders))
|
||||
mutators := make([]Mutator, len(_c.builders))
|
||||
for i := range _c.builders {
|
||||
func(i int, root context.Context) {
|
||||
builder := ptcb.builders[i]
|
||||
builder := _c.builders[i]
|
||||
builder.defaults()
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*PasswordTokenMutation)
|
||||
|
|
@ -235,16 +200,16 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
|
|||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
var err error
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, ptcb.builders[i+1].mutation)
|
||||
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
|
||||
} else {
|
||||
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
|
||||
// Invoke the actual operation on the latest mutation in the chain.
|
||||
if err = sqlgraph.BatchCreate(ctx, ptcb.driver, spec); err != nil {
|
||||
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -252,11 +217,11 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
|
|||
return nil, err
|
||||
}
|
||||
mutation.id = &nodes[i].ID
|
||||
mutation.done = true
|
||||
if specs[i].ID.Value != nil {
|
||||
id := specs[i].ID.Value.(int64)
|
||||
nodes[i].ID = int(id)
|
||||
}
|
||||
mutation.done = true
|
||||
return nodes[i], nil
|
||||
})
|
||||
for i := len(builder.hooks) - 1; i >= 0; i-- {
|
||||
|
|
@ -266,7 +231,7 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
|
|||
}(i, ctx)
|
||||
}
|
||||
if len(mutators) > 0 {
|
||||
if _, err := mutators[0].Mutate(ctx, ptcb.builders[0].mutation); err != nil {
|
||||
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -274,8 +239,8 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
|
|||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (ptcb *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken {
|
||||
v, err := ptcb.Save(ctx)
|
||||
func (_c *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken {
|
||||
v, err := _c.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -283,14 +248,14 @@ func (ptcb *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (ptcb *PasswordTokenCreateBulk) Exec(ctx context.Context) error {
|
||||
_, err := ptcb.Save(ctx)
|
||||
func (_c *PasswordTokenCreateBulk) Exec(ctx context.Context) error {
|
||||
_, err := _c.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptcb *PasswordTokenCreateBulk) ExecX(ctx context.Context) {
|
||||
if err := ptcb.Exec(ctx); err != nil {
|
||||
func (_c *PasswordTokenCreateBulk) ExecX(ctx context.Context) {
|
||||
if err := _c.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/predicate"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
)
|
||||
|
||||
// PasswordTokenDelete is the builder for deleting a PasswordToken entity.
|
||||
|
|
@ -21,80 +20,56 @@ type PasswordTokenDelete struct {
|
|||
}
|
||||
|
||||
// Where appends a list predicates to the PasswordTokenDelete builder.
|
||||
func (ptd *PasswordTokenDelete) Where(ps ...predicate.PasswordToken) *PasswordTokenDelete {
|
||||
ptd.mutation.Where(ps...)
|
||||
return ptd
|
||||
func (_d *PasswordTokenDelete) Where(ps ...predicate.PasswordToken) *PasswordTokenDelete {
|
||||
_d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (ptd *PasswordTokenDelete) Exec(ctx context.Context) (int, error) {
|
||||
var (
|
||||
err error
|
||||
affected int
|
||||
)
|
||||
if len(ptd.hooks) == 0 {
|
||||
affected, err = ptd.sqlExec(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*PasswordTokenMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
ptd.mutation = mutation
|
||||
affected, err = ptd.sqlExec(ctx)
|
||||
mutation.done = true
|
||||
return affected, err
|
||||
})
|
||||
for i := len(ptd.hooks) - 1; i >= 0; i-- {
|
||||
if ptd.hooks[i] == nil {
|
||||
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = ptd.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, ptd.mutation); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return affected, err
|
||||
func (_d *PasswordTokenDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptd *PasswordTokenDelete) ExecX(ctx context.Context) int {
|
||||
n, err := ptd.Exec(ctx)
|
||||
func (_d *PasswordTokenDelete) ExecX(ctx context.Context) int {
|
||||
n, err := _d.Exec(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (ptd *PasswordTokenDelete) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := &sqlgraph.DeleteSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: passwordtoken.Table,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
},
|
||||
}
|
||||
if ps := ptd.mutation.predicates; len(ps) > 0 {
|
||||
func (_d *PasswordTokenDelete) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := sqlgraph.NewDeleteSpec(passwordtoken.Table, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
|
||||
if ps := _d.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlgraph.DeleteNodes(ctx, ptd.driver, _spec)
|
||||
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
|
||||
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
_d.mutation.done = true
|
||||
return affected, err
|
||||
}
|
||||
|
||||
// PasswordTokenDeleteOne is the builder for deleting a single PasswordToken entity.
|
||||
type PasswordTokenDeleteOne struct {
|
||||
ptd *PasswordTokenDelete
|
||||
_d *PasswordTokenDelete
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the PasswordTokenDelete builder.
|
||||
func (_d *PasswordTokenDeleteOne) Where(ps ...predicate.PasswordToken) *PasswordTokenDeleteOne {
|
||||
_d._d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query.
|
||||
func (ptdo *PasswordTokenDeleteOne) Exec(ctx context.Context) error {
|
||||
n, err := ptdo.ptd.Exec(ctx)
|
||||
func (_d *PasswordTokenDeleteOne) Exec(ctx context.Context) error {
|
||||
n, err := _d._d.Exec(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
|
@ -106,6 +81,8 @@ func (ptdo *PasswordTokenDeleteOne) Exec(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptdo *PasswordTokenDeleteOne) ExecX(ctx context.Context) {
|
||||
ptdo.ptd.ExecX(ctx)
|
||||
func (_d *PasswordTokenDeleteOne) ExecX(ctx context.Context) {
|
||||
if err := _d.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
|
|
@ -6,14 +6,14 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/predicate"
|
||||
"goweb/ent/user"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// PasswordTokenUpdate is the builder for updating PasswordToken entities.
|
||||
|
|
@ -24,94 +24,77 @@ type PasswordTokenUpdate struct {
|
|||
}
|
||||
|
||||
// Where appends a list predicates to the PasswordTokenUpdate builder.
|
||||
func (ptu *PasswordTokenUpdate) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdate {
|
||||
ptu.mutation.Where(ps...)
|
||||
return ptu
|
||||
func (_u *PasswordTokenUpdate) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdate {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetHash sets the "hash" field.
|
||||
func (ptu *PasswordTokenUpdate) SetHash(s string) *PasswordTokenUpdate {
|
||||
ptu.mutation.SetHash(s)
|
||||
return ptu
|
||||
// SetToken sets the "token" field.
|
||||
func (_u *PasswordTokenUpdate) SetToken(v string) *PasswordTokenUpdate {
|
||||
_u.mutation.SetToken(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableToken sets the "token" field if the given value is not nil.
|
||||
func (_u *PasswordTokenUpdate) SetNillableToken(v *string) *PasswordTokenUpdate {
|
||||
if v != nil {
|
||||
_u.SetToken(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUserID sets the "user_id" field.
|
||||
func (_u *PasswordTokenUpdate) SetUserID(v int) *PasswordTokenUpdate {
|
||||
_u.mutation.SetUserID(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableUserID sets the "user_id" field if the given value is not nil.
|
||||
func (_u *PasswordTokenUpdate) SetNillableUserID(v *int) *PasswordTokenUpdate {
|
||||
if v != nil {
|
||||
_u.SetUserID(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
func (ptu *PasswordTokenUpdate) SetCreatedAt(t time.Time) *PasswordTokenUpdate {
|
||||
ptu.mutation.SetCreatedAt(t)
|
||||
return ptu
|
||||
func (_u *PasswordTokenUpdate) SetCreatedAt(v time.Time) *PasswordTokenUpdate {
|
||||
_u.mutation.SetCreatedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||
func (ptu *PasswordTokenUpdate) SetNillableCreatedAt(t *time.Time) *PasswordTokenUpdate {
|
||||
if t != nil {
|
||||
ptu.SetCreatedAt(*t)
|
||||
func (_u *PasswordTokenUpdate) SetNillableCreatedAt(v *time.Time) *PasswordTokenUpdate {
|
||||
if v != nil {
|
||||
_u.SetCreatedAt(*v)
|
||||
}
|
||||
return ptu
|
||||
}
|
||||
|
||||
// SetUserID sets the "user" edge to the User entity by ID.
|
||||
func (ptu *PasswordTokenUpdate) SetUserID(id int) *PasswordTokenUpdate {
|
||||
ptu.mutation.SetUserID(id)
|
||||
return ptu
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUser sets the "user" edge to the User entity.
|
||||
func (ptu *PasswordTokenUpdate) SetUser(u *User) *PasswordTokenUpdate {
|
||||
return ptu.SetUserID(u.ID)
|
||||
func (_u *PasswordTokenUpdate) SetUser(v *User) *PasswordTokenUpdate {
|
||||
return _u.SetUserID(v.ID)
|
||||
}
|
||||
|
||||
// Mutation returns the PasswordTokenMutation object of the builder.
|
||||
func (ptu *PasswordTokenUpdate) Mutation() *PasswordTokenMutation {
|
||||
return ptu.mutation
|
||||
func (_u *PasswordTokenUpdate) Mutation() *PasswordTokenMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearUser clears the "user" edge to the User entity.
|
||||
func (ptu *PasswordTokenUpdate) ClearUser() *PasswordTokenUpdate {
|
||||
ptu.mutation.ClearUser()
|
||||
return ptu
|
||||
func (_u *PasswordTokenUpdate) ClearUser() *PasswordTokenUpdate {
|
||||
_u.mutation.ClearUser()
|
||||
return _u
|
||||
}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (ptu *PasswordTokenUpdate) Save(ctx context.Context) (int, error) {
|
||||
var (
|
||||
err error
|
||||
affected int
|
||||
)
|
||||
if len(ptu.hooks) == 0 {
|
||||
if err = ptu.check(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
affected, err = ptu.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*PasswordTokenMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = ptu.check(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ptu.mutation = mutation
|
||||
affected, err = ptu.sqlSave(ctx)
|
||||
mutation.done = true
|
||||
return affected, err
|
||||
})
|
||||
for i := len(ptu.hooks) - 1; i >= 0; i-- {
|
||||
if ptu.hooks[i] == nil {
|
||||
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = ptu.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, ptu.mutation); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return affected, err
|
||||
func (_u *PasswordTokenUpdate) Save(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (ptu *PasswordTokenUpdate) SaveX(ctx context.Context) int {
|
||||
affected, err := ptu.Save(ctx)
|
||||
func (_u *PasswordTokenUpdate) SaveX(ctx context.Context) int {
|
||||
affected, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -119,64 +102,50 @@ func (ptu *PasswordTokenUpdate) SaveX(ctx context.Context) int {
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (ptu *PasswordTokenUpdate) Exec(ctx context.Context) error {
|
||||
_, err := ptu.Save(ctx)
|
||||
func (_u *PasswordTokenUpdate) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptu *PasswordTokenUpdate) ExecX(ctx context.Context) {
|
||||
if err := ptu.Exec(ctx); err != nil {
|
||||
func (_u *PasswordTokenUpdate) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (ptu *PasswordTokenUpdate) check() error {
|
||||
if v, ok := ptu.mutation.Hash(); ok {
|
||||
if err := passwordtoken.HashValidator(v); err != nil {
|
||||
return &ValidationError{Name: "hash", err: fmt.Errorf("ent: validator failed for field \"hash\": %w", err)}
|
||||
func (_u *PasswordTokenUpdate) check() error {
|
||||
if v, ok := _u.mutation.Token(); ok {
|
||||
if err := passwordtoken.TokenValidator(v); err != nil {
|
||||
return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := ptu.mutation.UserID(); ptu.mutation.UserCleared() && !ok {
|
||||
return errors.New("ent: clearing a required unique edge \"user\"")
|
||||
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
|
||||
return errors.New(`ent: clearing a required unique edge "PasswordToken.user"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
_spec := &sqlgraph.UpdateSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: passwordtoken.Table,
|
||||
Columns: passwordtoken.Columns,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
},
|
||||
func (_u *PasswordTokenUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
if ps := ptu.mutation.predicates; len(ps) > 0 {
|
||||
_spec := sqlgraph.NewUpdateSpec(passwordtoken.Table, passwordtoken.Columns, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := ptu.mutation.Hash(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldHash,
|
||||
})
|
||||
if value, ok := _u.mutation.Token(); ok {
|
||||
_spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
|
||||
}
|
||||
if value, ok := ptu.mutation.CreatedAt(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeTime,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldCreatedAt,
|
||||
})
|
||||
if value, ok := _u.mutation.CreatedAt(); ok {
|
||||
_spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
|
||||
}
|
||||
if ptu.mutation.UserCleared() {
|
||||
if _u.mutation.UserCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
|
|
@ -184,15 +153,12 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
|
|||
Columns: []string{passwordtoken.UserColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ptu.mutation.UserIDs(); len(nodes) > 0 {
|
||||
if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
|
|
@ -200,10 +166,7 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
|
|||
Columns: []string{passwordtoken.UserColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -211,15 +174,16 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
|
|||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if n, err = sqlgraph.UpdateNodes(ctx, ptu.driver, _spec); err != nil {
|
||||
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{passwordtoken.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
// PasswordTokenUpdateOne is the builder for updating a single PasswordToken entity.
|
||||
|
|
@ -230,96 +194,85 @@ type PasswordTokenUpdateOne struct {
|
|||
mutation *PasswordTokenMutation
|
||||
}
|
||||
|
||||
// SetHash sets the "hash" field.
|
||||
func (ptuo *PasswordTokenUpdateOne) SetHash(s string) *PasswordTokenUpdateOne {
|
||||
ptuo.mutation.SetHash(s)
|
||||
return ptuo
|
||||
// SetToken sets the "token" field.
|
||||
func (_u *PasswordTokenUpdateOne) SetToken(v string) *PasswordTokenUpdateOne {
|
||||
_u.mutation.SetToken(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableToken sets the "token" field if the given value is not nil.
|
||||
func (_u *PasswordTokenUpdateOne) SetNillableToken(v *string) *PasswordTokenUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetToken(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUserID sets the "user_id" field.
|
||||
func (_u *PasswordTokenUpdateOne) SetUserID(v int) *PasswordTokenUpdateOne {
|
||||
_u.mutation.SetUserID(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableUserID sets the "user_id" field if the given value is not nil.
|
||||
func (_u *PasswordTokenUpdateOne) SetNillableUserID(v *int) *PasswordTokenUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetUserID(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
func (ptuo *PasswordTokenUpdateOne) SetCreatedAt(t time.Time) *PasswordTokenUpdateOne {
|
||||
ptuo.mutation.SetCreatedAt(t)
|
||||
return ptuo
|
||||
func (_u *PasswordTokenUpdateOne) SetCreatedAt(v time.Time) *PasswordTokenUpdateOne {
|
||||
_u.mutation.SetCreatedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||
func (ptuo *PasswordTokenUpdateOne) SetNillableCreatedAt(t *time.Time) *PasswordTokenUpdateOne {
|
||||
if t != nil {
|
||||
ptuo.SetCreatedAt(*t)
|
||||
func (_u *PasswordTokenUpdateOne) SetNillableCreatedAt(v *time.Time) *PasswordTokenUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetCreatedAt(*v)
|
||||
}
|
||||
return ptuo
|
||||
}
|
||||
|
||||
// SetUserID sets the "user" edge to the User entity by ID.
|
||||
func (ptuo *PasswordTokenUpdateOne) SetUserID(id int) *PasswordTokenUpdateOne {
|
||||
ptuo.mutation.SetUserID(id)
|
||||
return ptuo
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUser sets the "user" edge to the User entity.
|
||||
func (ptuo *PasswordTokenUpdateOne) SetUser(u *User) *PasswordTokenUpdateOne {
|
||||
return ptuo.SetUserID(u.ID)
|
||||
func (_u *PasswordTokenUpdateOne) SetUser(v *User) *PasswordTokenUpdateOne {
|
||||
return _u.SetUserID(v.ID)
|
||||
}
|
||||
|
||||
// Mutation returns the PasswordTokenMutation object of the builder.
|
||||
func (ptuo *PasswordTokenUpdateOne) Mutation() *PasswordTokenMutation {
|
||||
return ptuo.mutation
|
||||
func (_u *PasswordTokenUpdateOne) Mutation() *PasswordTokenMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearUser clears the "user" edge to the User entity.
|
||||
func (ptuo *PasswordTokenUpdateOne) ClearUser() *PasswordTokenUpdateOne {
|
||||
ptuo.mutation.ClearUser()
|
||||
return ptuo
|
||||
func (_u *PasswordTokenUpdateOne) ClearUser() *PasswordTokenUpdateOne {
|
||||
_u.mutation.ClearUser()
|
||||
return _u
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the PasswordTokenUpdate builder.
|
||||
func (_u *PasswordTokenUpdateOne) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdateOne {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||
// The default is selecting all fields defined in the entity schema.
|
||||
func (ptuo *PasswordTokenUpdateOne) Select(field string, fields ...string) *PasswordTokenUpdateOne {
|
||||
ptuo.fields = append([]string{field}, fields...)
|
||||
return ptuo
|
||||
func (_u *PasswordTokenUpdateOne) Select(field string, fields ...string) *PasswordTokenUpdateOne {
|
||||
_u.fields = append([]string{field}, fields...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Save executes the query and returns the updated PasswordToken entity.
|
||||
func (ptuo *PasswordTokenUpdateOne) Save(ctx context.Context) (*PasswordToken, error) {
|
||||
var (
|
||||
err error
|
||||
node *PasswordToken
|
||||
)
|
||||
if len(ptuo.hooks) == 0 {
|
||||
if err = ptuo.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err = ptuo.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*PasswordTokenMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = ptuo.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptuo.mutation = mutation
|
||||
node, err = ptuo.sqlSave(ctx)
|
||||
mutation.done = true
|
||||
return node, err
|
||||
})
|
||||
for i := len(ptuo.hooks) - 1; i >= 0; i-- {
|
||||
if ptuo.hooks[i] == nil {
|
||||
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = ptuo.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, ptuo.mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return node, err
|
||||
func (_u *PasswordTokenUpdateOne) Save(ctx context.Context) (*PasswordToken, error) {
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (ptuo *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken {
|
||||
node, err := ptuo.Save(ctx)
|
||||
func (_u *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken {
|
||||
node, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -327,48 +280,42 @@ func (ptuo *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken {
|
|||
}
|
||||
|
||||
// Exec executes the query on the entity.
|
||||
func (ptuo *PasswordTokenUpdateOne) Exec(ctx context.Context) error {
|
||||
_, err := ptuo.Save(ctx)
|
||||
func (_u *PasswordTokenUpdateOne) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ptuo *PasswordTokenUpdateOne) ExecX(ctx context.Context) {
|
||||
if err := ptuo.Exec(ctx); err != nil {
|
||||
func (_u *PasswordTokenUpdateOne) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (ptuo *PasswordTokenUpdateOne) check() error {
|
||||
if v, ok := ptuo.mutation.Hash(); ok {
|
||||
if err := passwordtoken.HashValidator(v); err != nil {
|
||||
return &ValidationError{Name: "hash", err: fmt.Errorf("ent: validator failed for field \"hash\": %w", err)}
|
||||
func (_u *PasswordTokenUpdateOne) check() error {
|
||||
if v, ok := _u.mutation.Token(); ok {
|
||||
if err := passwordtoken.TokenValidator(v); err != nil {
|
||||
return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := ptuo.mutation.UserID(); ptuo.mutation.UserCleared() && !ok {
|
||||
return errors.New("ent: clearing a required unique edge \"user\"")
|
||||
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
|
||||
return errors.New(`ent: clearing a required unique edge "PasswordToken.user"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *PasswordToken, err error) {
|
||||
_spec := &sqlgraph.UpdateSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: passwordtoken.Table,
|
||||
Columns: passwordtoken.Columns,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
},
|
||||
func (_u *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *PasswordToken, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
id, ok := ptuo.mutation.ID()
|
||||
_spec := sqlgraph.NewUpdateSpec(passwordtoken.Table, passwordtoken.Columns, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
|
||||
id, ok := _u.mutation.ID()
|
||||
if !ok {
|
||||
return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing PasswordToken.ID for update")}
|
||||
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "PasswordToken.id" for update`)}
|
||||
}
|
||||
_spec.Node.ID.Value = id
|
||||
if fields := ptuo.fields; len(fields) > 0 {
|
||||
if fields := _u.fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, passwordtoken.FieldID)
|
||||
for _, f := range fields {
|
||||
|
|
@ -380,28 +327,20 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
|
|||
}
|
||||
}
|
||||
}
|
||||
if ps := ptuo.mutation.predicates; len(ps) > 0 {
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := ptuo.mutation.Hash(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldHash,
|
||||
})
|
||||
if value, ok := _u.mutation.Token(); ok {
|
||||
_spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
|
||||
}
|
||||
if value, ok := ptuo.mutation.CreatedAt(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeTime,
|
||||
Value: value,
|
||||
Column: passwordtoken.FieldCreatedAt,
|
||||
})
|
||||
if value, ok := _u.mutation.CreatedAt(); ok {
|
||||
_spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
|
||||
}
|
||||
if ptuo.mutation.UserCleared() {
|
||||
if _u.mutation.UserCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
|
|
@ -409,15 +348,12 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
|
|||
Columns: []string{passwordtoken.UserColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ptuo.mutation.UserIDs(); len(nodes) > 0 {
|
||||
if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
|
|
@ -425,10 +361,7 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
|
|||
Columns: []string{passwordtoken.UserColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -436,16 +369,17 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
|
|||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
_node = &PasswordToken{config: ptuo.config}
|
||||
_node = &PasswordToken{config: _u.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
if err = sqlgraph.UpdateNode(ctx, ptuo.driver, _spec); err != nil {
|
||||
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{passwordtoken.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package predicate
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
// The schema-stitching logic is generated in goweb/ent/runtime/runtime.go
|
||||
// The schema-stitching logic is generated in github.com/mikestefanello/pagoda/ent/runtime/runtime.go
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/schema"
|
||||
"goweb/ent/user"
|
||||
"time"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/schema"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// The init function reads all schema descriptors with runtime code
|
||||
// (default values, validators, hooks and policies) and stitches it
|
||||
// to their package variables.
|
||||
func init() {
|
||||
passwordtokenHooks := schema.PasswordToken{}.Hooks()
|
||||
passwordtoken.Hooks[0] = passwordtokenHooks[0]
|
||||
passwordtokenFields := schema.PasswordToken{}.Fields()
|
||||
_ = passwordtokenFields
|
||||
// passwordtokenDescHash is the schema descriptor for hash field.
|
||||
passwordtokenDescHash := passwordtokenFields[0].Descriptor()
|
||||
// passwordtoken.HashValidator is a validator for the "hash" field. It is called by the builders before save.
|
||||
passwordtoken.HashValidator = passwordtokenDescHash.Validators[0].(func(string) error)
|
||||
// passwordtokenDescToken is the schema descriptor for token field.
|
||||
passwordtokenDescToken := passwordtokenFields[0].Descriptor()
|
||||
// passwordtoken.TokenValidator is a validator for the "token" field. It is called by the builders before save.
|
||||
passwordtoken.TokenValidator = passwordtokenDescToken.Validators[0].(func(string) error)
|
||||
// passwordtokenDescCreatedAt is the schema descriptor for created_at field.
|
||||
passwordtokenDescCreatedAt := passwordtokenFields[1].Descriptor()
|
||||
passwordtokenDescCreatedAt := passwordtokenFields[2].Descriptor()
|
||||
// passwordtoken.DefaultCreatedAt holds the default value on creation for the created_at field.
|
||||
passwordtoken.DefaultCreatedAt = passwordtokenDescCreatedAt.Default.(func() time.Time)
|
||||
userHooks := schema.User{}.Hooks()
|
||||
|
|
@ -34,18 +37,40 @@ func init() {
|
|||
// userDescEmail is the schema descriptor for email field.
|
||||
userDescEmail := userFields[1].Descriptor()
|
||||
// user.EmailValidator is a validator for the "email" field. It is called by the builders before save.
|
||||
user.EmailValidator = userDescEmail.Validators[0].(func(string) error)
|
||||
user.EmailValidator = func() func(string) error {
|
||||
validators := userDescEmail.Validators
|
||||
fns := [...]func(string) error{
|
||||
validators[0].(func(string) error),
|
||||
validators[1].(func(string) error),
|
||||
}
|
||||
return func(email string) error {
|
||||
for _, fn := range fns {
|
||||
if err := fn(email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
// userDescPassword is the schema descriptor for password field.
|
||||
userDescPassword := userFields[2].Descriptor()
|
||||
// user.PasswordValidator is a validator for the "password" field. It is called by the builders before save.
|
||||
user.PasswordValidator = userDescPassword.Validators[0].(func(string) error)
|
||||
// userDescVerified is the schema descriptor for verified field.
|
||||
userDescVerified := userFields[3].Descriptor()
|
||||
// user.DefaultVerified holds the default value on creation for the verified field.
|
||||
user.DefaultVerified = userDescVerified.Default.(bool)
|
||||
// userDescAdmin is the schema descriptor for admin field.
|
||||
userDescAdmin := userFields[4].Descriptor()
|
||||
// user.DefaultAdmin holds the default value on creation for the admin field.
|
||||
user.DefaultAdmin = userDescAdmin.Default.(bool)
|
||||
// userDescCreatedAt is the schema descriptor for created_at field.
|
||||
userDescCreatedAt := userFields[3].Descriptor()
|
||||
userDescCreatedAt := userFields[5].Descriptor()
|
||||
// user.DefaultCreatedAt holds the default value on creation for the created_at field.
|
||||
user.DefaultCreatedAt = userDescCreatedAt.Default.(func() time.Time)
|
||||
}
|
||||
|
||||
const (
|
||||
Version = "v0.9.1" // Version of ent codegen.
|
||||
Sum = "h1:IG8andyeD79GG24U8Q+1Y45hQXj6gY5evSBcva5gtBk=" // Sum of ent codegen.
|
||||
Version = "v0.14.5" // Version of ent codegen.
|
||||
Sum = "h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=" // Sum of ent codegen.
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/edge"
|
||||
"entgo.io/ent/schema/field"
|
||||
ge "github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/hook"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// PasswordToken holds the schema definition for the PasswordToken entity.
|
||||
|
|
@ -16,9 +20,10 @@ type PasswordToken struct {
|
|||
// Fields of the PasswordToken.
|
||||
func (PasswordToken) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("hash").
|
||||
field.String("token").
|
||||
Sensitive().
|
||||
NotEmpty(),
|
||||
field.Int("user_id"),
|
||||
field.Time("created_at").
|
||||
Default(time.Now),
|
||||
}
|
||||
|
|
@ -28,7 +33,30 @@ func (PasswordToken) Fields() []ent.Field {
|
|||
func (PasswordToken) Edges() []ent.Edge {
|
||||
return []ent.Edge{
|
||||
edge.To("user", User.Type).
|
||||
Field("user_id").
|
||||
Required().
|
||||
Unique(),
|
||||
}
|
||||
}
|
||||
|
||||
// Hooks of the PasswordToken.
|
||||
func (PasswordToken) Hooks() []ent.Hook {
|
||||
return []ent.Hook{
|
||||
hook.On(
|
||||
func(next ent.Mutator) ent.Mutator {
|
||||
return hook.PasswordTokenFunc(func(ctx context.Context, m *ge.PasswordTokenMutation) (ent.Value, error) {
|
||||
if v, exists := m.Token(); exists {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(v), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m.SetToken(string(hash))
|
||||
}
|
||||
return next.Mutate(ctx, m)
|
||||
})
|
||||
},
|
||||
// Limit the hook only for these operations.
|
||||
ent.OpCreate|ent.OpUpdate|ent.OpUpdateOne,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ package schema
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ge "goweb/ent"
|
||||
"goweb/ent/hook"
|
||||
ge "github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/hook"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/edge"
|
||||
|
|
@ -25,10 +27,18 @@ func (User) Fields() []ent.Field {
|
|||
NotEmpty(),
|
||||
field.String("email").
|
||||
NotEmpty().
|
||||
Unique(),
|
||||
Unique().
|
||||
Validate(func(s string) error {
|
||||
_, err := mail.ParseAddress(s)
|
||||
return err
|
||||
}),
|
||||
field.String("password").
|
||||
Sensitive().
|
||||
NotEmpty(),
|
||||
field.Bool("verified").
|
||||
Default(false),
|
||||
field.Bool("admin").
|
||||
Default(false),
|
||||
field.Time("created_at").
|
||||
Default(time.Now).
|
||||
Immutable(),
|
||||
|
|
@ -52,6 +62,14 @@ func (User) Hooks() []ent.Hook {
|
|||
if v, exists := m.Email(); exists {
|
||||
m.SetEmail(strings.ToLower(v))
|
||||
}
|
||||
|
||||
if v, exists := m.Password(); exists {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(v), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m.SetPassword(string(hash))
|
||||
}
|
||||
return next.Mutate(ctx, m)
|
||||
})
|
||||
},
|
||||
|
|
|
|||
50
ent/tx.go
50
ent/tx.go
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
|
|
@ -20,19 +20,13 @@ type Tx struct {
|
|||
// lazily loaded.
|
||||
client *Client
|
||||
clientOnce sync.Once
|
||||
|
||||
// completion callbacks.
|
||||
mu sync.Mutex
|
||||
onCommit []CommitHook
|
||||
onRollback []RollbackHook
|
||||
|
||||
// ctx lives for the life of the transaction. It is
|
||||
// the same context used by the underlying connection.
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
type (
|
||||
// Committer is the interface that wraps the Committer method.
|
||||
// Committer is the interface that wraps the Commit method.
|
||||
Committer interface {
|
||||
Commit(context.Context, *Tx) error
|
||||
}
|
||||
|
|
@ -46,7 +40,7 @@ type (
|
|||
// and returns a Committer. For example:
|
||||
//
|
||||
// hook := func(next ent.Committer) ent.Committer {
|
||||
// return ent.CommitFunc(func(context.Context, tx *ent.Tx) error {
|
||||
// return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error {
|
||||
// // Do some stuff before.
|
||||
// if err := next.Commit(ctx, tx); err != nil {
|
||||
// return err
|
||||
|
|
@ -70,9 +64,9 @@ func (tx *Tx) Commit() error {
|
|||
var fn Committer = CommitFunc(func(context.Context, *Tx) error {
|
||||
return txDriver.tx.Commit()
|
||||
})
|
||||
tx.mu.Lock()
|
||||
hooks := append([]CommitHook(nil), tx.onCommit...)
|
||||
tx.mu.Unlock()
|
||||
txDriver.mu.Lock()
|
||||
hooks := append([]CommitHook(nil), txDriver.onCommit...)
|
||||
txDriver.mu.Unlock()
|
||||
for i := len(hooks) - 1; i >= 0; i-- {
|
||||
fn = hooks[i](fn)
|
||||
}
|
||||
|
|
@ -81,13 +75,14 @@ func (tx *Tx) Commit() error {
|
|||
|
||||
// OnCommit adds a hook to call on commit.
|
||||
func (tx *Tx) OnCommit(f CommitHook) {
|
||||
tx.mu.Lock()
|
||||
defer tx.mu.Unlock()
|
||||
tx.onCommit = append(tx.onCommit, f)
|
||||
txDriver := tx.config.driver.(*txDriver)
|
||||
txDriver.mu.Lock()
|
||||
txDriver.onCommit = append(txDriver.onCommit, f)
|
||||
txDriver.mu.Unlock()
|
||||
}
|
||||
|
||||
type (
|
||||
// Rollbacker is the interface that wraps the Rollbacker method.
|
||||
// Rollbacker is the interface that wraps the Rollback method.
|
||||
Rollbacker interface {
|
||||
Rollback(context.Context, *Tx) error
|
||||
}
|
||||
|
|
@ -101,7 +96,7 @@ type (
|
|||
// and returns a Rollbacker. For example:
|
||||
//
|
||||
// hook := func(next ent.Rollbacker) ent.Rollbacker {
|
||||
// return ent.RollbackFunc(func(context.Context, tx *ent.Tx) error {
|
||||
// return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error {
|
||||
// // Do some stuff before.
|
||||
// if err := next.Rollback(ctx, tx); err != nil {
|
||||
// return err
|
||||
|
|
@ -125,9 +120,9 @@ func (tx *Tx) Rollback() error {
|
|||
var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error {
|
||||
return txDriver.tx.Rollback()
|
||||
})
|
||||
tx.mu.Lock()
|
||||
hooks := append([]RollbackHook(nil), tx.onRollback...)
|
||||
tx.mu.Unlock()
|
||||
txDriver.mu.Lock()
|
||||
hooks := append([]RollbackHook(nil), txDriver.onRollback...)
|
||||
txDriver.mu.Unlock()
|
||||
for i := len(hooks) - 1; i >= 0; i-- {
|
||||
fn = hooks[i](fn)
|
||||
}
|
||||
|
|
@ -136,9 +131,10 @@ func (tx *Tx) Rollback() error {
|
|||
|
||||
// OnRollback adds a hook to call on rollback.
|
||||
func (tx *Tx) OnRollback(f RollbackHook) {
|
||||
tx.mu.Lock()
|
||||
defer tx.mu.Unlock()
|
||||
tx.onRollback = append(tx.onRollback, f)
|
||||
txDriver := tx.config.driver.(*txDriver)
|
||||
txDriver.mu.Lock()
|
||||
txDriver.onRollback = append(txDriver.onRollback, f)
|
||||
txDriver.mu.Unlock()
|
||||
}
|
||||
|
||||
// Client returns a Client that binds to current transaction.
|
||||
|
|
@ -171,6 +167,10 @@ type txDriver struct {
|
|||
drv dialect.Driver
|
||||
// tx is the underlying transaction.
|
||||
tx dialect.Tx
|
||||
// completion hooks.
|
||||
mu sync.Mutex
|
||||
onCommit []CommitHook
|
||||
onRollback []RollbackHook
|
||||
}
|
||||
|
||||
// newTx creates a new transactional driver.
|
||||
|
|
@ -201,12 +201,12 @@ func (*txDriver) Commit() error { return nil }
|
|||
func (*txDriver) Rollback() error { return nil }
|
||||
|
||||
// Exec calls tx.Exec.
|
||||
func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error {
|
||||
func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error {
|
||||
return tx.tx.Exec(ctx, query, args, v)
|
||||
}
|
||||
|
||||
// Query calls tx.Query.
|
||||
func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error {
|
||||
func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error {
|
||||
return tx.tx.Query(ctx, query, args, v)
|
||||
}
|
||||
|
||||
|
|
|
|||
101
ent/user.go
101
ent/user.go
|
|
@ -1,14 +1,15 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"goweb/ent/user"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// User is the model entity for the User schema.
|
||||
|
|
@ -22,11 +23,16 @@ type User struct {
|
|||
Email string `json:"email,omitempty"`
|
||||
// Password holds the value of the "password" field.
|
||||
Password string `json:"-"`
|
||||
// Verified holds the value of the "verified" field.
|
||||
Verified bool `json:"verified,omitempty"`
|
||||
// Admin holds the value of the "admin" field.
|
||||
Admin bool `json:"admin,omitempty"`
|
||||
// CreatedAt holds the value of the "created_at" field.
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the UserQuery when eager-loading is set.
|
||||
Edges UserEdges `json:"edges"`
|
||||
Edges UserEdges `json:"edges"`
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// UserEdges holds the relations/edges for other nodes in the graph.
|
||||
|
|
@ -48,10 +54,12 @@ func (e UserEdges) OwnerOrErr() ([]*PasswordToken, error) {
|
|||
}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*User) scanValues(columns []string) ([]interface{}, error) {
|
||||
values := make([]interface{}, len(columns))
|
||||
func (*User) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case user.FieldVerified, user.FieldAdmin:
|
||||
values[i] = new(sql.NullBool)
|
||||
case user.FieldID:
|
||||
values[i] = new(sql.NullInt64)
|
||||
case user.FieldName, user.FieldEmail, user.FieldPassword:
|
||||
|
|
@ -59,7 +67,7 @@ func (*User) scanValues(columns []string) ([]interface{}, error) {
|
|||
case user.FieldCreatedAt:
|
||||
values[i] = new(sql.NullTime)
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type User", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
|
|
@ -67,7 +75,7 @@ func (*User) scanValues(columns []string) ([]interface{}, error) {
|
|||
|
||||
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||
// to the User fields.
|
||||
func (u *User) assignValues(columns []string, values []interface{}) error {
|
||||
func (_m *User) assignValues(columns []string, values []any) error {
|
||||
if m, n := len(values), len(columns); m < n {
|
||||
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||
}
|
||||
|
|
@ -78,80 +86,103 @@ func (u *User) assignValues(columns []string, values []interface{}) error {
|
|||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field id", value)
|
||||
}
|
||||
u.ID = int(value.Int64)
|
||||
_m.ID = int(value.Int64)
|
||||
case user.FieldName:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field name", values[i])
|
||||
} else if value.Valid {
|
||||
u.Name = value.String
|
||||
_m.Name = value.String
|
||||
}
|
||||
case user.FieldEmail:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field email", values[i])
|
||||
} else if value.Valid {
|
||||
u.Email = value.String
|
||||
_m.Email = value.String
|
||||
}
|
||||
case user.FieldPassword:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field password", values[i])
|
||||
} else if value.Valid {
|
||||
u.Password = value.String
|
||||
_m.Password = value.String
|
||||
}
|
||||
case user.FieldVerified:
|
||||
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field verified", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Verified = value.Bool
|
||||
}
|
||||
case user.FieldAdmin:
|
||||
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field admin", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Admin = value.Bool
|
||||
}
|
||||
case user.FieldCreatedAt:
|
||||
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||
} else if value.Valid {
|
||||
u.CreatedAt = value.Time
|
||||
_m.CreatedAt = value.Time
|
||||
}
|
||||
default:
|
||||
_m.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the User.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (_m *User) Value(name string) (ent.Value, error) {
|
||||
return _m.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryOwner queries the "owner" edge of the User entity.
|
||||
func (u *User) QueryOwner() *PasswordTokenQuery {
|
||||
return (&UserClient{config: u.config}).QueryOwner(u)
|
||||
func (_m *User) QueryOwner() *PasswordTokenQuery {
|
||||
return NewUserClient(_m.config).QueryOwner(_m)
|
||||
}
|
||||
|
||||
// Update returns a builder for updating this User.
|
||||
// Note that you need to call User.Unwrap() before calling this method if this User
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
func (u *User) Update() *UserUpdateOne {
|
||||
return (&UserClient{config: u.config}).UpdateOne(u)
|
||||
func (_m *User) Update() *UserUpdateOne {
|
||||
return NewUserClient(_m.config).UpdateOne(_m)
|
||||
}
|
||||
|
||||
// Unwrap unwraps the User entity that was returned from a transaction after it was closed,
|
||||
// so that all future queries will be executed through the driver which created the transaction.
|
||||
func (u *User) Unwrap() *User {
|
||||
tx, ok := u.config.driver.(*txDriver)
|
||||
func (_m *User) Unwrap() *User {
|
||||
_tx, ok := _m.config.driver.(*txDriver)
|
||||
if !ok {
|
||||
panic("ent: User is not a transactional entity")
|
||||
}
|
||||
u.config.driver = tx.drv
|
||||
return u
|
||||
_m.config.driver = _tx.drv
|
||||
return _m
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer.
|
||||
func (u *User) String() string {
|
||||
func (_m *User) String() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("User(")
|
||||
builder.WriteString(fmt.Sprintf("id=%v", u.ID))
|
||||
builder.WriteString(", name=")
|
||||
builder.WriteString(u.Name)
|
||||
builder.WriteString(", email=")
|
||||
builder.WriteString(u.Email)
|
||||
builder.WriteString(", password=<sensitive>")
|
||||
builder.WriteString(", created_at=")
|
||||
builder.WriteString(u.CreatedAt.Format(time.ANSIC))
|
||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||
builder.WriteString("name=")
|
||||
builder.WriteString(_m.Name)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("email=")
|
||||
builder.WriteString(_m.Email)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("password=<sensitive>")
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("verified=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.Verified))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("admin=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.Admin))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("created_at=")
|
||||
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// Users is a parsable slice of User.
|
||||
type Users []*User
|
||||
|
||||
func (u Users) config(cfg config) {
|
||||
for _i := range u {
|
||||
u[_i].config = cfg
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package user
|
||||
|
||||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -19,6 +21,10 @@ const (
|
|||
FieldEmail = "email"
|
||||
// FieldPassword holds the string denoting the password field in the database.
|
||||
FieldPassword = "password"
|
||||
// FieldVerified holds the string denoting the verified field in the database.
|
||||
FieldVerified = "verified"
|
||||
// FieldAdmin holds the string denoting the admin field in the database.
|
||||
FieldAdmin = "admin"
|
||||
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
||||
FieldCreatedAt = "created_at"
|
||||
// EdgeOwner holds the string denoting the owner edge name in mutations.
|
||||
|
|
@ -31,7 +37,7 @@ const (
|
|||
// It exists in this package in order to avoid circular dependency with the "passwordtoken" package.
|
||||
OwnerInverseTable = "password_tokens"
|
||||
// OwnerColumn is the table column denoting the owner relation/edge.
|
||||
OwnerColumn = "password_token_user"
|
||||
OwnerColumn = "user_id"
|
||||
)
|
||||
|
||||
// Columns holds all SQL columns for user fields.
|
||||
|
|
@ -40,6 +46,8 @@ var Columns = []string{
|
|||
FieldName,
|
||||
FieldEmail,
|
||||
FieldPassword,
|
||||
FieldVerified,
|
||||
FieldAdmin,
|
||||
FieldCreatedAt,
|
||||
}
|
||||
|
||||
|
|
@ -57,8 +65,7 @@ func ValidColumn(column string) bool {
|
|||
// package on the initialization of the application. Therefore,
|
||||
// it should be imported in the main as follows:
|
||||
//
|
||||
// import _ "goweb/ent/runtime"
|
||||
//
|
||||
// import _ "github.com/mikestefanello/pagoda/ent/runtime"
|
||||
var (
|
||||
Hooks [1]ent.Hook
|
||||
// NameValidator is a validator for the "name" field. It is called by the builders before save.
|
||||
|
|
@ -67,6 +74,69 @@ var (
|
|||
EmailValidator func(string) error
|
||||
// PasswordValidator is a validator for the "password" field. It is called by the builders before save.
|
||||
PasswordValidator func(string) error
|
||||
// DefaultVerified holds the default value on creation for the "verified" field.
|
||||
DefaultVerified bool
|
||||
// DefaultAdmin holds the default value on creation for the "admin" field.
|
||||
DefaultAdmin bool
|
||||
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
||||
DefaultCreatedAt func() time.Time
|
||||
)
|
||||
|
||||
// OrderOption defines the ordering options for the User queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByName orders the results by the name field.
|
||||
func ByName(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldName, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByEmail orders the results by the email field.
|
||||
func ByEmail(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldEmail, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByPassword orders the results by the password field.
|
||||
func ByPassword(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldPassword, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByVerified orders the results by the verified field.
|
||||
func ByVerified(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldVerified, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByAdmin orders the results by the admin field.
|
||||
func ByAdmin(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldAdmin, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByOwnerCount orders the results by owner count.
|
||||
func ByOwnerCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, newOwnerStep(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByOwner orders the results by owner terms.
|
||||
func ByOwner(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newOwnerStep(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
func newOwnerStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(OwnerInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,533 +1,343 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"goweb/ent/predicate"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
func ID(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDEQ applies the EQ predicate on the ID field.
|
||||
func IDEQ(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDNEQ applies the NEQ predicate on the ID field.
|
||||
func IDNEQ(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldNEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDIn applies the In predicate on the ID field.
|
||||
func IDIn(ids ...int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(ids) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
v := make([]interface{}, len(ids))
|
||||
for i := range v {
|
||||
v[i] = ids[i]
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldID), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDNotIn applies the NotIn predicate on the ID field.
|
||||
func IDNotIn(ids ...int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(ids) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
v := make([]interface{}, len(ids))
|
||||
for i := range v {
|
||||
v[i] = ids[i]
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldID), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldNotIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDGT applies the GT predicate on the ID field.
|
||||
func IDGT(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldGT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDGTE applies the GTE predicate on the ID field.
|
||||
func IDGTE(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldGTE(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLT applies the LT predicate on the ID field.
|
||||
func IDLT(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldLT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLTE applies the LTE predicate on the ID field.
|
||||
func IDLTE(id int) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldID), id))
|
||||
})
|
||||
return predicate.User(sql.FieldLTE(FieldID, id))
|
||||
}
|
||||
|
||||
// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
|
||||
func Name(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldName, v))
|
||||
}
|
||||
|
||||
// Email applies equality check predicate on the "email" field. It's identical to EmailEQ.
|
||||
func Email(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldEmail, v))
|
||||
}
|
||||
|
||||
// Password applies equality check predicate on the "password" field. It's identical to PasswordEQ.
|
||||
func Password(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldPassword, v))
|
||||
}
|
||||
|
||||
// Verified applies equality check predicate on the "verified" field. It's identical to VerifiedEQ.
|
||||
func Verified(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldEQ(FieldVerified, v))
|
||||
}
|
||||
|
||||
// Admin applies equality check predicate on the "admin" field. It's identical to AdminEQ.
|
||||
func Admin(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldEQ(FieldAdmin, v))
|
||||
}
|
||||
|
||||
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
|
||||
func CreatedAt(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// NameEQ applies the EQ predicate on the "name" field.
|
||||
func NameEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldName, v))
|
||||
}
|
||||
|
||||
// NameNEQ applies the NEQ predicate on the "name" field.
|
||||
func NameNEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldNEQ(FieldName, v))
|
||||
}
|
||||
|
||||
// NameIn applies the In predicate on the "name" field.
|
||||
func NameIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldName), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldIn(FieldName, vs...))
|
||||
}
|
||||
|
||||
// NameNotIn applies the NotIn predicate on the "name" field.
|
||||
func NameNotIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldName), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldNotIn(FieldName, vs...))
|
||||
}
|
||||
|
||||
// NameGT applies the GT predicate on the "name" field.
|
||||
func NameGT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGT(FieldName, v))
|
||||
}
|
||||
|
||||
// NameGTE applies the GTE predicate on the "name" field.
|
||||
func NameGTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGTE(FieldName, v))
|
||||
}
|
||||
|
||||
// NameLT applies the LT predicate on the "name" field.
|
||||
func NameLT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLT(FieldName, v))
|
||||
}
|
||||
|
||||
// NameLTE applies the LTE predicate on the "name" field.
|
||||
func NameLTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLTE(FieldName, v))
|
||||
}
|
||||
|
||||
// NameContains applies the Contains predicate on the "name" field.
|
||||
func NameContains(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.Contains(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContains(FieldName, v))
|
||||
}
|
||||
|
||||
// NameHasPrefix applies the HasPrefix predicate on the "name" field.
|
||||
func NameHasPrefix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasPrefix(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasPrefix(FieldName, v))
|
||||
}
|
||||
|
||||
// NameHasSuffix applies the HasSuffix predicate on the "name" field.
|
||||
func NameHasSuffix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasSuffix(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasSuffix(FieldName, v))
|
||||
}
|
||||
|
||||
// NameEqualFold applies the EqualFold predicate on the "name" field.
|
||||
func NameEqualFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EqualFold(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEqualFold(FieldName, v))
|
||||
}
|
||||
|
||||
// NameContainsFold applies the ContainsFold predicate on the "name" field.
|
||||
func NameContainsFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.ContainsFold(s.C(FieldName), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContainsFold(FieldName, v))
|
||||
}
|
||||
|
||||
// EmailEQ applies the EQ predicate on the "email" field.
|
||||
func EmailEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailNEQ applies the NEQ predicate on the "email" field.
|
||||
func EmailNEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldNEQ(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailIn applies the In predicate on the "email" field.
|
||||
func EmailIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldEmail), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldIn(FieldEmail, vs...))
|
||||
}
|
||||
|
||||
// EmailNotIn applies the NotIn predicate on the "email" field.
|
||||
func EmailNotIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldEmail), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldNotIn(FieldEmail, vs...))
|
||||
}
|
||||
|
||||
// EmailGT applies the GT predicate on the "email" field.
|
||||
func EmailGT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGT(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailGTE applies the GTE predicate on the "email" field.
|
||||
func EmailGTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGTE(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailLT applies the LT predicate on the "email" field.
|
||||
func EmailLT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLT(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailLTE applies the LTE predicate on the "email" field.
|
||||
func EmailLTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLTE(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailContains applies the Contains predicate on the "email" field.
|
||||
func EmailContains(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.Contains(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContains(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailHasPrefix applies the HasPrefix predicate on the "email" field.
|
||||
func EmailHasPrefix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasPrefix(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasPrefix(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailHasSuffix applies the HasSuffix predicate on the "email" field.
|
||||
func EmailHasSuffix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasSuffix(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasSuffix(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailEqualFold applies the EqualFold predicate on the "email" field.
|
||||
func EmailEqualFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EqualFold(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEqualFold(FieldEmail, v))
|
||||
}
|
||||
|
||||
// EmailContainsFold applies the ContainsFold predicate on the "email" field.
|
||||
func EmailContainsFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.ContainsFold(s.C(FieldEmail), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContainsFold(FieldEmail, v))
|
||||
}
|
||||
|
||||
// PasswordEQ applies the EQ predicate on the "password" field.
|
||||
func PasswordEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordNEQ applies the NEQ predicate on the "password" field.
|
||||
func PasswordNEQ(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldNEQ(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordIn applies the In predicate on the "password" field.
|
||||
func PasswordIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldPassword), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldIn(FieldPassword, vs...))
|
||||
}
|
||||
|
||||
// PasswordNotIn applies the NotIn predicate on the "password" field.
|
||||
func PasswordNotIn(vs ...string) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldPassword), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldNotIn(FieldPassword, vs...))
|
||||
}
|
||||
|
||||
// PasswordGT applies the GT predicate on the "password" field.
|
||||
func PasswordGT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGT(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordGTE applies the GTE predicate on the "password" field.
|
||||
func PasswordGTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGTE(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordLT applies the LT predicate on the "password" field.
|
||||
func PasswordLT(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLT(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordLTE applies the LTE predicate on the "password" field.
|
||||
func PasswordLTE(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLTE(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordContains applies the Contains predicate on the "password" field.
|
||||
func PasswordContains(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.Contains(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContains(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordHasPrefix applies the HasPrefix predicate on the "password" field.
|
||||
func PasswordHasPrefix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasPrefix(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasPrefix(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordHasSuffix applies the HasSuffix predicate on the "password" field.
|
||||
func PasswordHasSuffix(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.HasSuffix(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldHasSuffix(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordEqualFold applies the EqualFold predicate on the "password" field.
|
||||
func PasswordEqualFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EqualFold(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEqualFold(FieldPassword, v))
|
||||
}
|
||||
|
||||
// PasswordContainsFold applies the ContainsFold predicate on the "password" field.
|
||||
func PasswordContainsFold(v string) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.ContainsFold(s.C(FieldPassword), v))
|
||||
})
|
||||
return predicate.User(sql.FieldContainsFold(FieldPassword, v))
|
||||
}
|
||||
|
||||
// VerifiedEQ applies the EQ predicate on the "verified" field.
|
||||
func VerifiedEQ(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldEQ(FieldVerified, v))
|
||||
}
|
||||
|
||||
// VerifiedNEQ applies the NEQ predicate on the "verified" field.
|
||||
func VerifiedNEQ(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldNEQ(FieldVerified, v))
|
||||
}
|
||||
|
||||
// AdminEQ applies the EQ predicate on the "admin" field.
|
||||
func AdminEQ(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldEQ(FieldAdmin, v))
|
||||
}
|
||||
|
||||
// AdminNEQ applies the NEQ predicate on the "admin" field.
|
||||
func AdminNEQ(v bool) predicate.User {
|
||||
return predicate.User(sql.FieldNEQ(FieldAdmin, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
|
||||
func CreatedAtNEQ(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldNEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtIn applies the In predicate on the "created_at" field.
|
||||
func CreatedAtIn(vs ...time.Time) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.In(s.C(FieldCreatedAt), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
|
||||
func CreatedAtNotIn(vs ...time.Time) predicate.User {
|
||||
v := make([]interface{}, len(vs))
|
||||
for i := range v {
|
||||
v[i] = vs[i]
|
||||
}
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
// if not arguments were provided, append the FALSE constants,
|
||||
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||
if len(v) == 0 {
|
||||
s.Where(sql.False())
|
||||
return
|
||||
}
|
||||
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
|
||||
})
|
||||
return predicate.User(sql.FieldNotIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtGT applies the GT predicate on the "created_at" field.
|
||||
func CreatedAtGT(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GT(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
|
||||
func CreatedAtGTE(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldGTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLT applies the LT predicate on the "created_at" field.
|
||||
func CreatedAtLT(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LT(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
|
||||
func CreatedAtLTE(v time.Time) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
|
||||
})
|
||||
return predicate.User(sql.FieldLTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// HasOwner applies the HasEdge predicate on the "owner" edge.
|
||||
|
|
@ -535,7 +345,6 @@ func HasOwner() predicate.User {
|
|||
return predicate.User(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(OwnerTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
|
|
@ -545,11 +354,7 @@ func HasOwner() predicate.User {
|
|||
// HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates).
|
||||
func HasOwnerWith(preds ...predicate.PasswordToken) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(OwnerInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
|
||||
)
|
||||
step := newOwnerStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
|
|
@ -560,32 +365,15 @@ func HasOwnerWith(preds ...predicate.PasswordToken) predicate.User {
|
|||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.User) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for _, p := range predicates {
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.User(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.User) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for i, p := range predicates {
|
||||
if i > 0 {
|
||||
s1.Or()
|
||||
}
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.User(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.User) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
p(s.Not())
|
||||
})
|
||||
return predicate.User(sql.NotPredicates(p))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
|
|
@ -6,12 +6,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/user"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// UserCreate is the builder for creating a User entity.
|
||||
|
|
@ -22,104 +22,96 @@ type UserCreate struct {
|
|||
}
|
||||
|
||||
// SetName sets the "name" field.
|
||||
func (uc *UserCreate) SetName(s string) *UserCreate {
|
||||
uc.mutation.SetName(s)
|
||||
return uc
|
||||
func (_c *UserCreate) SetName(v string) *UserCreate {
|
||||
_c.mutation.SetName(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetEmail sets the "email" field.
|
||||
func (uc *UserCreate) SetEmail(s string) *UserCreate {
|
||||
uc.mutation.SetEmail(s)
|
||||
return uc
|
||||
func (_c *UserCreate) SetEmail(v string) *UserCreate {
|
||||
_c.mutation.SetEmail(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetPassword sets the "password" field.
|
||||
func (uc *UserCreate) SetPassword(s string) *UserCreate {
|
||||
uc.mutation.SetPassword(s)
|
||||
return uc
|
||||
func (_c *UserCreate) SetPassword(v string) *UserCreate {
|
||||
_c.mutation.SetPassword(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetVerified sets the "verified" field.
|
||||
func (_c *UserCreate) SetVerified(v bool) *UserCreate {
|
||||
_c.mutation.SetVerified(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetNillableVerified sets the "verified" field if the given value is not nil.
|
||||
func (_c *UserCreate) SetNillableVerified(v *bool) *UserCreate {
|
||||
if v != nil {
|
||||
_c.SetVerified(*v)
|
||||
}
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetAdmin sets the "admin" field.
|
||||
func (_c *UserCreate) SetAdmin(v bool) *UserCreate {
|
||||
_c.mutation.SetAdmin(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetNillableAdmin sets the "admin" field if the given value is not nil.
|
||||
func (_c *UserCreate) SetNillableAdmin(v *bool) *UserCreate {
|
||||
if v != nil {
|
||||
_c.SetAdmin(*v)
|
||||
}
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the "created_at" field.
|
||||
func (uc *UserCreate) SetCreatedAt(t time.Time) *UserCreate {
|
||||
uc.mutation.SetCreatedAt(t)
|
||||
return uc
|
||||
func (_c *UserCreate) SetCreatedAt(v time.Time) *UserCreate {
|
||||
_c.mutation.SetCreatedAt(v)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||
func (uc *UserCreate) SetNillableCreatedAt(t *time.Time) *UserCreate {
|
||||
if t != nil {
|
||||
uc.SetCreatedAt(*t)
|
||||
func (_c *UserCreate) SetNillableCreatedAt(v *time.Time) *UserCreate {
|
||||
if v != nil {
|
||||
_c.SetCreatedAt(*v)
|
||||
}
|
||||
return uc
|
||||
return _c
|
||||
}
|
||||
|
||||
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
|
||||
func (uc *UserCreate) AddOwnerIDs(ids ...int) *UserCreate {
|
||||
uc.mutation.AddOwnerIDs(ids...)
|
||||
return uc
|
||||
func (_c *UserCreate) AddOwnerIDs(ids ...int) *UserCreate {
|
||||
_c.mutation.AddOwnerIDs(ids...)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AddOwner adds the "owner" edges to the PasswordToken entity.
|
||||
func (uc *UserCreate) AddOwner(p ...*PasswordToken) *UserCreate {
|
||||
ids := make([]int, len(p))
|
||||
for i := range p {
|
||||
ids[i] = p[i].ID
|
||||
func (_c *UserCreate) AddOwner(v ...*PasswordToken) *UserCreate {
|
||||
ids := make([]int, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return uc.AddOwnerIDs(ids...)
|
||||
return _c.AddOwnerIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uc *UserCreate) Mutation() *UserMutation {
|
||||
return uc.mutation
|
||||
func (_c *UserCreate) Mutation() *UserMutation {
|
||||
return _c.mutation
|
||||
}
|
||||
|
||||
// Save creates the User in the database.
|
||||
func (uc *UserCreate) Save(ctx context.Context) (*User, error) {
|
||||
var (
|
||||
err error
|
||||
node *User
|
||||
)
|
||||
if err := uc.defaults(); err != nil {
|
||||
func (_c *UserCreate) Save(ctx context.Context) (*User, error) {
|
||||
if err := _c.defaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(uc.hooks) == 0 {
|
||||
if err = uc.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err = uc.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*UserMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = uc.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uc.mutation = mutation
|
||||
if node, err = uc.sqlSave(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mutation.id = &node.ID
|
||||
mutation.done = true
|
||||
return node, err
|
||||
})
|
||||
for i := len(uc.hooks) - 1; i >= 0; i-- {
|
||||
if uc.hooks[i] == nil {
|
||||
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = uc.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, uc.mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return node, err
|
||||
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
func (uc *UserCreate) SaveX(ctx context.Context) *User {
|
||||
v, err := uc.Save(ctx)
|
||||
func (_c *UserCreate) SaveX(ctx context.Context) *User {
|
||||
v, err := _c.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -127,119 +119,124 @@ func (uc *UserCreate) SaveX(ctx context.Context) *User {
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (uc *UserCreate) Exec(ctx context.Context) error {
|
||||
_, err := uc.Save(ctx)
|
||||
func (_c *UserCreate) Exec(ctx context.Context) error {
|
||||
_, err := _c.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (uc *UserCreate) ExecX(ctx context.Context) {
|
||||
if err := uc.Exec(ctx); err != nil {
|
||||
func (_c *UserCreate) ExecX(ctx context.Context) {
|
||||
if err := _c.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// defaults sets the default values of the builder before save.
|
||||
func (uc *UserCreate) defaults() error {
|
||||
if _, ok := uc.mutation.CreatedAt(); !ok {
|
||||
func (_c *UserCreate) defaults() error {
|
||||
if _, ok := _c.mutation.Verified(); !ok {
|
||||
v := user.DefaultVerified
|
||||
_c.mutation.SetVerified(v)
|
||||
}
|
||||
if _, ok := _c.mutation.Admin(); !ok {
|
||||
v := user.DefaultAdmin
|
||||
_c.mutation.SetAdmin(v)
|
||||
}
|
||||
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||
if user.DefaultCreatedAt == nil {
|
||||
return fmt.Errorf("ent: uninitialized user.DefaultCreatedAt (forgotten import ent/runtime?)")
|
||||
}
|
||||
v := user.DefaultCreatedAt()
|
||||
uc.mutation.SetCreatedAt(v)
|
||||
_c.mutation.SetCreatedAt(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (uc *UserCreate) check() error {
|
||||
if _, ok := uc.mutation.Name(); !ok {
|
||||
return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "name"`)}
|
||||
func (_c *UserCreate) check() error {
|
||||
if _, ok := _c.mutation.Name(); !ok {
|
||||
return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "User.name"`)}
|
||||
}
|
||||
if v, ok := uc.mutation.Name(); ok {
|
||||
if v, ok := _c.mutation.Name(); ok {
|
||||
if err := user.NameValidator(v); err != nil {
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "name": %w`, err)}
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := uc.mutation.Email(); !ok {
|
||||
return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "email"`)}
|
||||
if _, ok := _c.mutation.Email(); !ok {
|
||||
return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "User.email"`)}
|
||||
}
|
||||
if v, ok := uc.mutation.Email(); ok {
|
||||
if v, ok := _c.mutation.Email(); ok {
|
||||
if err := user.EmailValidator(v); err != nil {
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "email": %w`, err)}
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := uc.mutation.Password(); !ok {
|
||||
return &ValidationError{Name: "password", err: errors.New(`ent: missing required field "password"`)}
|
||||
if _, ok := _c.mutation.Password(); !ok {
|
||||
return &ValidationError{Name: "password", err: errors.New(`ent: missing required field "User.password"`)}
|
||||
}
|
||||
if v, ok := uc.mutation.Password(); ok {
|
||||
if v, ok := _c.mutation.Password(); ok {
|
||||
if err := user.PasswordValidator(v); err != nil {
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "password": %w`, err)}
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := uc.mutation.CreatedAt(); !ok {
|
||||
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)}
|
||||
if _, ok := _c.mutation.Verified(); !ok {
|
||||
return &ValidationError{Name: "verified", err: errors.New(`ent: missing required field "User.verified"`)}
|
||||
}
|
||||
if _, ok := _c.mutation.Admin(); !ok {
|
||||
return &ValidationError{Name: "admin", err: errors.New(`ent: missing required field "User.admin"`)}
|
||||
}
|
||||
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "User.created_at"`)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) {
|
||||
_node, _spec := uc.createSpec()
|
||||
if err := sqlgraph.CreateNode(ctx, uc.driver, _spec); err != nil {
|
||||
func (_c *UserCreate) sqlSave(ctx context.Context) (*User, error) {
|
||||
if err := _c.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_node, _spec := _c.createSpec()
|
||||
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
id := _spec.ID.Value.(int64)
|
||||
_node.ID = int(id)
|
||||
_c.mutation.id = &_node.ID
|
||||
_c.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
|
||||
func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
|
||||
var (
|
||||
_node = &User{config: uc.config}
|
||||
_spec = &sqlgraph.CreateSpec{
|
||||
Table: user.Table,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
}
|
||||
_node = &User{config: _c.config}
|
||||
_spec = sqlgraph.NewCreateSpec(user.Table, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
|
||||
)
|
||||
if value, ok := uc.mutation.Name(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldName,
|
||||
})
|
||||
if value, ok := _c.mutation.Name(); ok {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
_node.Name = value
|
||||
}
|
||||
if value, ok := uc.mutation.Email(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldEmail,
|
||||
})
|
||||
if value, ok := _c.mutation.Email(); ok {
|
||||
_spec.SetField(user.FieldEmail, field.TypeString, value)
|
||||
_node.Email = value
|
||||
}
|
||||
if value, ok := uc.mutation.Password(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldPassword,
|
||||
})
|
||||
if value, ok := _c.mutation.Password(); ok {
|
||||
_spec.SetField(user.FieldPassword, field.TypeString, value)
|
||||
_node.Password = value
|
||||
}
|
||||
if value, ok := uc.mutation.CreatedAt(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeTime,
|
||||
Value: value,
|
||||
Column: user.FieldCreatedAt,
|
||||
})
|
||||
if value, ok := _c.mutation.Verified(); ok {
|
||||
_spec.SetField(user.FieldVerified, field.TypeBool, value)
|
||||
_node.Verified = value
|
||||
}
|
||||
if value, ok := _c.mutation.Admin(); ok {
|
||||
_spec.SetField(user.FieldAdmin, field.TypeBool, value)
|
||||
_node.Admin = value
|
||||
}
|
||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||
_spec.SetField(user.FieldCreatedAt, field.TypeTime, value)
|
||||
_node.CreatedAt = value
|
||||
}
|
||||
if nodes := uc.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
if nodes := _c.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -247,10 +244,7 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -264,17 +258,21 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
|
|||
// UserCreateBulk is the builder for creating many User entities in bulk.
|
||||
type UserCreateBulk struct {
|
||||
config
|
||||
err error
|
||||
builders []*UserCreate
|
||||
}
|
||||
|
||||
// Save creates the User entities in the database.
|
||||
func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
||||
specs := make([]*sqlgraph.CreateSpec, len(ucb.builders))
|
||||
nodes := make([]*User, len(ucb.builders))
|
||||
mutators := make([]Mutator, len(ucb.builders))
|
||||
for i := range ucb.builders {
|
||||
func (_c *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
||||
if _c.err != nil {
|
||||
return nil, _c.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
|
||||
nodes := make([]*User, len(_c.builders))
|
||||
mutators := make([]Mutator, len(_c.builders))
|
||||
for i := range _c.builders {
|
||||
func(i int, root context.Context) {
|
||||
builder := ucb.builders[i]
|
||||
builder := _c.builders[i]
|
||||
builder.defaults()
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*UserMutation)
|
||||
|
|
@ -285,16 +283,16 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
|||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
var err error
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, ucb.builders[i+1].mutation)
|
||||
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
|
||||
} else {
|
||||
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
|
||||
// Invoke the actual operation on the latest mutation in the chain.
|
||||
if err = sqlgraph.BatchCreate(ctx, ucb.driver, spec); err != nil {
|
||||
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -302,11 +300,11 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
|||
return nil, err
|
||||
}
|
||||
mutation.id = &nodes[i].ID
|
||||
mutation.done = true
|
||||
if specs[i].ID.Value != nil {
|
||||
id := specs[i].ID.Value.(int64)
|
||||
nodes[i].ID = int(id)
|
||||
}
|
||||
mutation.done = true
|
||||
return nodes[i], nil
|
||||
})
|
||||
for i := len(builder.hooks) - 1; i >= 0; i-- {
|
||||
|
|
@ -316,7 +314,7 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
|||
}(i, ctx)
|
||||
}
|
||||
if len(mutators) > 0 {
|
||||
if _, err := mutators[0].Mutate(ctx, ucb.builders[0].mutation); err != nil {
|
||||
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -324,8 +322,8 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
|
|||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User {
|
||||
v, err := ucb.Save(ctx)
|
||||
func (_c *UserCreateBulk) SaveX(ctx context.Context) []*User {
|
||||
v, err := _c.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -333,14 +331,14 @@ func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User {
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (ucb *UserCreateBulk) Exec(ctx context.Context) error {
|
||||
_, err := ucb.Save(ctx)
|
||||
func (_c *UserCreateBulk) Exec(ctx context.Context) error {
|
||||
_, err := _c.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ucb *UserCreateBulk) ExecX(ctx context.Context) {
|
||||
if err := ucb.Exec(ctx); err != nil {
|
||||
func (_c *UserCreateBulk) ExecX(ctx context.Context) {
|
||||
if err := _c.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"goweb/ent/predicate"
|
||||
"goweb/ent/user"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// UserDelete is the builder for deleting a User entity.
|
||||
|
|
@ -21,80 +20,56 @@ type UserDelete struct {
|
|||
}
|
||||
|
||||
// Where appends a list predicates to the UserDelete builder.
|
||||
func (ud *UserDelete) Where(ps ...predicate.User) *UserDelete {
|
||||
ud.mutation.Where(ps...)
|
||||
return ud
|
||||
func (_d *UserDelete) Where(ps ...predicate.User) *UserDelete {
|
||||
_d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (ud *UserDelete) Exec(ctx context.Context) (int, error) {
|
||||
var (
|
||||
err error
|
||||
affected int
|
||||
)
|
||||
if len(ud.hooks) == 0 {
|
||||
affected, err = ud.sqlExec(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*UserMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
ud.mutation = mutation
|
||||
affected, err = ud.sqlExec(ctx)
|
||||
mutation.done = true
|
||||
return affected, err
|
||||
})
|
||||
for i := len(ud.hooks) - 1; i >= 0; i-- {
|
||||
if ud.hooks[i] == nil {
|
||||
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = ud.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, ud.mutation); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return affected, err
|
||||
func (_d *UserDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (ud *UserDelete) ExecX(ctx context.Context) int {
|
||||
n, err := ud.Exec(ctx)
|
||||
func (_d *UserDelete) ExecX(ctx context.Context) int {
|
||||
n, err := _d.Exec(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (ud *UserDelete) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := &sqlgraph.DeleteSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: user.Table,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
},
|
||||
}
|
||||
if ps := ud.mutation.predicates; len(ps) > 0 {
|
||||
func (_d *UserDelete) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := sqlgraph.NewDeleteSpec(user.Table, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
|
||||
if ps := _d.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlgraph.DeleteNodes(ctx, ud.driver, _spec)
|
||||
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
|
||||
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
_d.mutation.done = true
|
||||
return affected, err
|
||||
}
|
||||
|
||||
// UserDeleteOne is the builder for deleting a single User entity.
|
||||
type UserDeleteOne struct {
|
||||
ud *UserDelete
|
||||
_d *UserDelete
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the UserDelete builder.
|
||||
func (_d *UserDeleteOne) Where(ps ...predicate.User) *UserDeleteOne {
|
||||
_d._d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query.
|
||||
func (udo *UserDeleteOne) Exec(ctx context.Context) error {
|
||||
n, err := udo.ud.Exec(ctx)
|
||||
func (_d *UserDeleteOne) Exec(ctx context.Context) error {
|
||||
n, err := _d._d.Exec(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
|
@ -106,6 +81,8 @@ func (udo *UserDeleteOne) Exec(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (udo *UserDeleteOne) ExecX(ctx context.Context) {
|
||||
udo.ud.ExecX(ctx)
|
||||
func (_d *UserDeleteOne) ExecX(ctx context.Context) {
|
||||
if err := _d.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +1,18 @@
|
|||
// Code generated by entc, DO NOT EDIT.
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goweb/ent/passwordtoken"
|
||||
"goweb/ent/predicate"
|
||||
"goweb/ent/user"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/passwordtoken"
|
||||
"github.com/mikestefanello/pagoda/ent/predicate"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
)
|
||||
|
||||
// UserUpdate is the builder for updating User entities.
|
||||
|
|
@ -22,111 +23,130 @@ type UserUpdate struct {
|
|||
}
|
||||
|
||||
// Where appends a list predicates to the UserUpdate builder.
|
||||
func (uu *UserUpdate) Where(ps ...predicate.User) *UserUpdate {
|
||||
uu.mutation.Where(ps...)
|
||||
return uu
|
||||
func (_u *UserUpdate) Where(ps ...predicate.User) *UserUpdate {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetName sets the "name" field.
|
||||
func (uu *UserUpdate) SetName(s string) *UserUpdate {
|
||||
uu.mutation.SetName(s)
|
||||
return uu
|
||||
func (_u *UserUpdate) SetName(v string) *UserUpdate {
|
||||
_u.mutation.SetName(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableName sets the "name" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableName(v *string) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetName(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEmail sets the "email" field.
|
||||
func (uu *UserUpdate) SetEmail(s string) *UserUpdate {
|
||||
uu.mutation.SetEmail(s)
|
||||
return uu
|
||||
func (_u *UserUpdate) SetEmail(v string) *UserUpdate {
|
||||
_u.mutation.SetEmail(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEmail sets the "email" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableEmail(v *string) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetEmail(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPassword sets the "password" field.
|
||||
func (uu *UserUpdate) SetPassword(s string) *UserUpdate {
|
||||
uu.mutation.SetPassword(s)
|
||||
return uu
|
||||
func (_u *UserUpdate) SetPassword(v string) *UserUpdate {
|
||||
_u.mutation.SetPassword(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePassword sets the "password" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillablePassword(v *string) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetPassword(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetVerified sets the "verified" field.
|
||||
func (_u *UserUpdate) SetVerified(v bool) *UserUpdate {
|
||||
_u.mutation.SetVerified(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableVerified sets the "verified" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableVerified(v *bool) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetVerified(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetAdmin sets the "admin" field.
|
||||
func (_u *UserUpdate) SetAdmin(v bool) *UserUpdate {
|
||||
_u.mutation.SetAdmin(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableAdmin sets the "admin" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableAdmin(v *bool) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetAdmin(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
|
||||
func (uu *UserUpdate) AddOwnerIDs(ids ...int) *UserUpdate {
|
||||
uu.mutation.AddOwnerIDs(ids...)
|
||||
return uu
|
||||
func (_u *UserUpdate) AddOwnerIDs(ids ...int) *UserUpdate {
|
||||
_u.mutation.AddOwnerIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddOwner adds the "owner" edges to the PasswordToken entity.
|
||||
func (uu *UserUpdate) AddOwner(p ...*PasswordToken) *UserUpdate {
|
||||
ids := make([]int, len(p))
|
||||
for i := range p {
|
||||
ids[i] = p[i].ID
|
||||
func (_u *UserUpdate) AddOwner(v ...*PasswordToken) *UserUpdate {
|
||||
ids := make([]int, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return uu.AddOwnerIDs(ids...)
|
||||
return _u.AddOwnerIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uu *UserUpdate) Mutation() *UserMutation {
|
||||
return uu.mutation
|
||||
func (_u *UserUpdate) Mutation() *UserMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearOwner clears all "owner" edges to the PasswordToken entity.
|
||||
func (uu *UserUpdate) ClearOwner() *UserUpdate {
|
||||
uu.mutation.ClearOwner()
|
||||
return uu
|
||||
func (_u *UserUpdate) ClearOwner() *UserUpdate {
|
||||
_u.mutation.ClearOwner()
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs.
|
||||
func (uu *UserUpdate) RemoveOwnerIDs(ids ...int) *UserUpdate {
|
||||
uu.mutation.RemoveOwnerIDs(ids...)
|
||||
return uu
|
||||
func (_u *UserUpdate) RemoveOwnerIDs(ids ...int) *UserUpdate {
|
||||
_u.mutation.RemoveOwnerIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveOwner removes "owner" edges to PasswordToken entities.
|
||||
func (uu *UserUpdate) RemoveOwner(p ...*PasswordToken) *UserUpdate {
|
||||
ids := make([]int, len(p))
|
||||
for i := range p {
|
||||
ids[i] = p[i].ID
|
||||
func (_u *UserUpdate) RemoveOwner(v ...*PasswordToken) *UserUpdate {
|
||||
ids := make([]int, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return uu.RemoveOwnerIDs(ids...)
|
||||
return _u.RemoveOwnerIDs(ids...)
|
||||
}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (uu *UserUpdate) Save(ctx context.Context) (int, error) {
|
||||
var (
|
||||
err error
|
||||
affected int
|
||||
)
|
||||
if len(uu.hooks) == 0 {
|
||||
if err = uu.check(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
affected, err = uu.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*UserMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = uu.check(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uu.mutation = mutation
|
||||
affected, err = uu.sqlSave(ctx)
|
||||
mutation.done = true
|
||||
return affected, err
|
||||
})
|
||||
for i := len(uu.hooks) - 1; i >= 0; i-- {
|
||||
if uu.hooks[i] == nil {
|
||||
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = uu.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, uu.mutation); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return affected, err
|
||||
func (_u *UserUpdate) Save(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (uu *UserUpdate) SaveX(ctx context.Context) int {
|
||||
affected, err := uu.Save(ctx)
|
||||
func (_u *UserUpdate) SaveX(ctx context.Context) int {
|
||||
affected, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -134,78 +154,66 @@ func (uu *UserUpdate) SaveX(ctx context.Context) int {
|
|||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (uu *UserUpdate) Exec(ctx context.Context) error {
|
||||
_, err := uu.Save(ctx)
|
||||
func (_u *UserUpdate) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (uu *UserUpdate) ExecX(ctx context.Context) {
|
||||
if err := uu.Exec(ctx); err != nil {
|
||||
func (_u *UserUpdate) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (uu *UserUpdate) check() error {
|
||||
if v, ok := uu.mutation.Name(); ok {
|
||||
func (_u *UserUpdate) check() error {
|
||||
if v, ok := _u.mutation.Name(); ok {
|
||||
if err := user.NameValidator(v); err != nil {
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf("ent: validator failed for field \"name\": %w", err)}
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := uu.mutation.Email(); ok {
|
||||
if v, ok := _u.mutation.Email(); ok {
|
||||
if err := user.EmailValidator(v); err != nil {
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf("ent: validator failed for field \"email\": %w", err)}
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := uu.mutation.Password(); ok {
|
||||
if v, ok := _u.mutation.Password(); ok {
|
||||
if err := user.PasswordValidator(v); err != nil {
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf("ent: validator failed for field \"password\": %w", err)}
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
_spec := &sqlgraph.UpdateSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: user.Table,
|
||||
Columns: user.Columns,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
},
|
||||
func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
if ps := uu.mutation.predicates; len(ps) > 0 {
|
||||
_spec := sqlgraph.NewUpdateSpec(user.Table, user.Columns, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := uu.mutation.Name(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldName,
|
||||
})
|
||||
if value, ok := _u.mutation.Name(); ok {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := uu.mutation.Email(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldEmail,
|
||||
})
|
||||
if value, ok := _u.mutation.Email(); ok {
|
||||
_spec.SetField(user.FieldEmail, field.TypeString, value)
|
||||
}
|
||||
if value, ok := uu.mutation.Password(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldPassword,
|
||||
})
|
||||
if value, ok := _u.mutation.Password(); ok {
|
||||
_spec.SetField(user.FieldPassword, field.TypeString, value)
|
||||
}
|
||||
if uu.mutation.OwnerCleared() {
|
||||
if value, ok := _u.mutation.Verified(); ok {
|
||||
_spec.SetField(user.FieldVerified, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Admin(); ok {
|
||||
_spec.SetField(user.FieldAdmin, field.TypeBool, value)
|
||||
}
|
||||
if _u.mutation.OwnerCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -213,15 +221,12 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uu.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !uu.mutation.OwnerCleared() {
|
||||
if nodes := _u.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !_u.mutation.OwnerCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -229,10 +234,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -240,7 +242,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uu.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
if nodes := _u.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -248,10 +250,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -259,15 +258,16 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil {
|
||||
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{user.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
// UserUpdateOne is the builder for updating a single User entity.
|
||||
|
|
@ -279,112 +279,137 @@ type UserUpdateOne struct {
|
|||
}
|
||||
|
||||
// SetName sets the "name" field.
|
||||
func (uuo *UserUpdateOne) SetName(s string) *UserUpdateOne {
|
||||
uuo.mutation.SetName(s)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) SetName(v string) *UserUpdateOne {
|
||||
_u.mutation.SetName(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableName sets the "name" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableName(v *string) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetName(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEmail sets the "email" field.
|
||||
func (uuo *UserUpdateOne) SetEmail(s string) *UserUpdateOne {
|
||||
uuo.mutation.SetEmail(s)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) SetEmail(v string) *UserUpdateOne {
|
||||
_u.mutation.SetEmail(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEmail sets the "email" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableEmail(v *string) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetEmail(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPassword sets the "password" field.
|
||||
func (uuo *UserUpdateOne) SetPassword(s string) *UserUpdateOne {
|
||||
uuo.mutation.SetPassword(s)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) SetPassword(v string) *UserUpdateOne {
|
||||
_u.mutation.SetPassword(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePassword sets the "password" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillablePassword(v *string) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetPassword(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetVerified sets the "verified" field.
|
||||
func (_u *UserUpdateOne) SetVerified(v bool) *UserUpdateOne {
|
||||
_u.mutation.SetVerified(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableVerified sets the "verified" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableVerified(v *bool) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetVerified(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetAdmin sets the "admin" field.
|
||||
func (_u *UserUpdateOne) SetAdmin(v bool) *UserUpdateOne {
|
||||
_u.mutation.SetAdmin(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableAdmin sets the "admin" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableAdmin(v *bool) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetAdmin(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
|
||||
func (uuo *UserUpdateOne) AddOwnerIDs(ids ...int) *UserUpdateOne {
|
||||
uuo.mutation.AddOwnerIDs(ids...)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) AddOwnerIDs(ids ...int) *UserUpdateOne {
|
||||
_u.mutation.AddOwnerIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddOwner adds the "owner" edges to the PasswordToken entity.
|
||||
func (uuo *UserUpdateOne) AddOwner(p ...*PasswordToken) *UserUpdateOne {
|
||||
ids := make([]int, len(p))
|
||||
for i := range p {
|
||||
ids[i] = p[i].ID
|
||||
func (_u *UserUpdateOne) AddOwner(v ...*PasswordToken) *UserUpdateOne {
|
||||
ids := make([]int, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return uuo.AddOwnerIDs(ids...)
|
||||
return _u.AddOwnerIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uuo *UserUpdateOne) Mutation() *UserMutation {
|
||||
return uuo.mutation
|
||||
func (_u *UserUpdateOne) Mutation() *UserMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearOwner clears all "owner" edges to the PasswordToken entity.
|
||||
func (uuo *UserUpdateOne) ClearOwner() *UserUpdateOne {
|
||||
uuo.mutation.ClearOwner()
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) ClearOwner() *UserUpdateOne {
|
||||
_u.mutation.ClearOwner()
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs.
|
||||
func (uuo *UserUpdateOne) RemoveOwnerIDs(ids ...int) *UserUpdateOne {
|
||||
uuo.mutation.RemoveOwnerIDs(ids...)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) RemoveOwnerIDs(ids ...int) *UserUpdateOne {
|
||||
_u.mutation.RemoveOwnerIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveOwner removes "owner" edges to PasswordToken entities.
|
||||
func (uuo *UserUpdateOne) RemoveOwner(p ...*PasswordToken) *UserUpdateOne {
|
||||
ids := make([]int, len(p))
|
||||
for i := range p {
|
||||
ids[i] = p[i].ID
|
||||
func (_u *UserUpdateOne) RemoveOwner(v ...*PasswordToken) *UserUpdateOne {
|
||||
ids := make([]int, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return uuo.RemoveOwnerIDs(ids...)
|
||||
return _u.RemoveOwnerIDs(ids...)
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the UserUpdate builder.
|
||||
func (_u *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||
// The default is selecting all fields defined in the entity schema.
|
||||
func (uuo *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne {
|
||||
uuo.fields = append([]string{field}, fields...)
|
||||
return uuo
|
||||
func (_u *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne {
|
||||
_u.fields = append([]string{field}, fields...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Save executes the query and returns the updated User entity.
|
||||
func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) {
|
||||
var (
|
||||
err error
|
||||
node *User
|
||||
)
|
||||
if len(uuo.hooks) == 0 {
|
||||
if err = uuo.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err = uuo.sqlSave(ctx)
|
||||
} else {
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*UserMutation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err = uuo.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uuo.mutation = mutation
|
||||
node, err = uuo.sqlSave(ctx)
|
||||
mutation.done = true
|
||||
return node, err
|
||||
})
|
||||
for i := len(uuo.hooks) - 1; i >= 0; i-- {
|
||||
if uuo.hooks[i] == nil {
|
||||
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = uuo.hooks[i](mut)
|
||||
}
|
||||
if _, err := mut.Mutate(ctx, uuo.mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return node, err
|
||||
func (_u *UserUpdateOne) Save(ctx context.Context) (*User, error) {
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User {
|
||||
node, err := uuo.Save(ctx)
|
||||
func (_u *UserUpdateOne) SaveX(ctx context.Context) *User {
|
||||
node, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -392,55 +417,49 @@ func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User {
|
|||
}
|
||||
|
||||
// Exec executes the query on the entity.
|
||||
func (uuo *UserUpdateOne) Exec(ctx context.Context) error {
|
||||
_, err := uuo.Save(ctx)
|
||||
func (_u *UserUpdateOne) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (uuo *UserUpdateOne) ExecX(ctx context.Context) {
|
||||
if err := uuo.Exec(ctx); err != nil {
|
||||
func (_u *UserUpdateOne) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (uuo *UserUpdateOne) check() error {
|
||||
if v, ok := uuo.mutation.Name(); ok {
|
||||
func (_u *UserUpdateOne) check() error {
|
||||
if v, ok := _u.mutation.Name(); ok {
|
||||
if err := user.NameValidator(v); err != nil {
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf("ent: validator failed for field \"name\": %w", err)}
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := uuo.mutation.Email(); ok {
|
||||
if v, ok := _u.mutation.Email(); ok {
|
||||
if err := user.EmailValidator(v); err != nil {
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf("ent: validator failed for field \"email\": %w", err)}
|
||||
return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := uuo.mutation.Password(); ok {
|
||||
if v, ok := _u.mutation.Password(); ok {
|
||||
if err := user.PasswordValidator(v); err != nil {
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf("ent: validator failed for field \"password\": %w", err)}
|
||||
return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
|
||||
_spec := &sqlgraph.UpdateSpec{
|
||||
Node: &sqlgraph.NodeSpec{
|
||||
Table: user.Table,
|
||||
Columns: user.Columns,
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: user.FieldID,
|
||||
},
|
||||
},
|
||||
func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
id, ok := uuo.mutation.ID()
|
||||
_spec := sqlgraph.NewUpdateSpec(user.Table, user.Columns, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
|
||||
id, ok := _u.mutation.ID()
|
||||
if !ok {
|
||||
return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing User.ID for update")}
|
||||
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "User.id" for update`)}
|
||||
}
|
||||
_spec.Node.ID.Value = id
|
||||
if fields := uuo.fields; len(fields) > 0 {
|
||||
if fields := _u.fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, user.FieldID)
|
||||
for _, f := range fields {
|
||||
|
|
@ -452,35 +471,29 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
}
|
||||
}
|
||||
}
|
||||
if ps := uuo.mutation.predicates; len(ps) > 0 {
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := uuo.mutation.Name(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldName,
|
||||
})
|
||||
if value, ok := _u.mutation.Name(); ok {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := uuo.mutation.Email(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldEmail,
|
||||
})
|
||||
if value, ok := _u.mutation.Email(); ok {
|
||||
_spec.SetField(user.FieldEmail, field.TypeString, value)
|
||||
}
|
||||
if value, ok := uuo.mutation.Password(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
Value: value,
|
||||
Column: user.FieldPassword,
|
||||
})
|
||||
if value, ok := _u.mutation.Password(); ok {
|
||||
_spec.SetField(user.FieldPassword, field.TypeString, value)
|
||||
}
|
||||
if uuo.mutation.OwnerCleared() {
|
||||
if value, ok := _u.mutation.Verified(); ok {
|
||||
_spec.SetField(user.FieldVerified, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Admin(); ok {
|
||||
_spec.SetField(user.FieldAdmin, field.TypeBool, value)
|
||||
}
|
||||
if _u.mutation.OwnerCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -488,15 +501,12 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uuo.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !uuo.mutation.OwnerCleared() {
|
||||
if nodes := _u.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !_u.mutation.OwnerCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -504,10 +514,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -515,7 +522,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uuo.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
if nodes := _u.mutation.OwnerIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: true,
|
||||
|
|
@ -523,10 +530,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
Columns: []string{user.OwnerColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: &sqlgraph.FieldSpec{
|
||||
Type: field.TypeInt,
|
||||
Column: passwordtoken.FieldID,
|
||||
},
|
||||
IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
|
|
@ -534,16 +538,17 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
|||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
_node = &User{config: uuo.config}
|
||||
_node = &User{config: _u.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
if err = sqlgraph.UpdateNode(ctx, uuo.driver, _spec); err != nil {
|
||||
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{user.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{err.Error(), err}
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package funcmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"goweb/config"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/labstack/gommon/random"
|
||||
)
|
||||
|
||||
var (
|
||||
// CacheBuster stores a random string used as a cache buster for static files.
|
||||
CacheBuster = random.String(10)
|
||||
)
|
||||
|
||||
// GetFuncMap provides a template function map
|
||||
func GetFuncMap() template.FuncMap {
|
||||
// See http://masterminds.github.io/sprig/ for available funcs
|
||||
funcMap := sprig.FuncMap()
|
||||
|
||||
// Provide a list of custom functions
|
||||
// Expand this as you add more functions to this package
|
||||
// Avoid using a name already in use by sprig
|
||||
f := template.FuncMap{
|
||||
"hasField": HasField,
|
||||
"file": File,
|
||||
"link": Link,
|
||||
}
|
||||
|
||||
for k, v := range f {
|
||||
funcMap[k] = v
|
||||
}
|
||||
|
||||
return funcMap
|
||||
}
|
||||
|
||||
// HasField checks if an interface contains a given field
|
||||
func HasField(v interface{}, name string) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if rv.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
return rv.FieldByName(name).IsValid()
|
||||
}
|
||||
|
||||
// File appends a cache buster to a given filepath so it can remain cached until the app is restarted
|
||||
func File(filepath string) string {
|
||||
return fmt.Sprintf("/%s/%s?v=%s", config.StaticPrefix, filepath, CacheBuster)
|
||||
}
|
||||
|
||||
// Link outputs HTML for a link element, providing the ability to dynamically set the active class
|
||||
func Link(url, text, currentPath string, classes ...string) template.HTML {
|
||||
if currentPath == url {
|
||||
classes = append(classes, "is-active")
|
||||
}
|
||||
|
||||
html := fmt.Sprintf(`<a class="%s" href="%s">%s</a>`, strings.Join(classes, " "), url, text)
|
||||
return template.HTML(html)
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package funcmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"goweb/config"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHasField(t *testing.T) {
|
||||
type example struct {
|
||||
name string
|
||||
}
|
||||
var e example
|
||||
assert.True(t, HasField(e, "name"))
|
||||
assert.False(t, HasField(e, "abcd"))
|
||||
}
|
||||
|
||||
func TestLink(t *testing.T) {
|
||||
link := string(Link("/abc", "Text", "/abc"))
|
||||
expected := `<a class="is-active" href="/abc">Text</a>`
|
||||
assert.Equal(t, expected, link)
|
||||
|
||||
link = string(Link("/abc", "Text", "/abc", "first", "second"))
|
||||
expected = `<a class="first second is-active" href="/abc">Text</a>`
|
||||
assert.Equal(t, expected, link)
|
||||
|
||||
link = string(Link("/abc", "Text", "/def"))
|
||||
expected = `<a class="" href="/abc">Text</a>`
|
||||
assert.Equal(t, expected, link)
|
||||
}
|
||||
|
||||
func TestGetFuncMap(t *testing.T) {
|
||||
file := File("test.png")
|
||||
expected := fmt.Sprintf("/%s/test.png?v=%s", config.StaticPrefix, CacheBuster)
|
||||
assert.Equal(t, expected, file)
|
||||
}
|
||||
130
go.mod
130
go.mod
|
|
@ -1,78 +1,68 @@
|
|||
module goweb
|
||||
module github.com/mikestefanello/pagoda
|
||||
|
||||
go 1.17
|
||||
go 1.24.6
|
||||
|
||||
require (
|
||||
entgo.io/ent v0.9.1
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/eko/gocache/v2 v2.1.0
|
||||
github.com/go-playground/assert/v2 v2.0.1
|
||||
github.com/go-playground/validator/v10 v10.9.0
|
||||
github.com/go-redis/redis/v8 v8.11.4
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/jackc/pgx/v4 v4.14.1
|
||||
github.com/joeshaw/envdecode v0.0.0-20200121155833-099f1fc765bd
|
||||
github.com/labstack/echo-contrib v0.11.0
|
||||
github.com/labstack/echo/v4 v4.6.1
|
||||
github.com/labstack/gommon v0.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
||||
entgo.io/ent v0.14.5
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/go-playground/validator/v10 v10.29.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/gorilla/context v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/labstack/echo/v4 v4.14.0
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/maypok86/otter v1.2.4
|
||||
github.com/mikestefanello/backlite v0.6.0
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.46.0
|
||||
maragu.dev/gomponents v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.10.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.9.1 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/pegasus-kv/thrift v0.13.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.10.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.25.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/sirupsen/logrus v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
ariga.io/atlas v0.38.0 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gammazero/deque v1.2.0 // indirect
|
||||
github.com/go-openapi/inflect v0.21.5 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
google.golang.org/appengine v1.6.1 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/zclconf/go-cty v1.17.0 // indirect
|
||||
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.31.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
82
htmx/htmx.go
82
htmx/htmx.go
|
|
@ -1,82 +0,0 @@
|
|||
package htmx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Headers (https://htmx.org/docs/#requests)
|
||||
const (
|
||||
HeaderRequest = "HX-Request"
|
||||
HeaderBoosted = "HX-Boosted"
|
||||
HeaderTrigger = "HX-Trigger"
|
||||
HeaderTriggerName = "HX-Trigger-Name"
|
||||
HeaderTriggerAfterSwap = "HX-Trigger-After-Swap"
|
||||
HeaderTriggerAfterSettle = "HX-Trigger-After-Settle"
|
||||
HeaderTarget = "HX-Target"
|
||||
HeaderPrompt = "HX-Prompt"
|
||||
HeaderPush = "HX-Push"
|
||||
HeaderRedirect = "HX-Redirect"
|
||||
HeaderRefresh = "HX-Refresh"
|
||||
)
|
||||
|
||||
type (
|
||||
// Request contains data that HTMX provides during requests
|
||||
Request struct {
|
||||
Enabled bool
|
||||
Boosted bool
|
||||
Trigger string
|
||||
TriggerName string
|
||||
Target string
|
||||
Prompt string
|
||||
}
|
||||
|
||||
// Response contain data that the server can communicate back to HTMX
|
||||
Response struct {
|
||||
Push string
|
||||
Redirect string
|
||||
Refresh bool
|
||||
Trigger string
|
||||
TriggerAfterSwap string
|
||||
TriggerAfterSettle string
|
||||
NoContent bool
|
||||
}
|
||||
)
|
||||
|
||||
// GetRequest extracts HTMX data from the request
|
||||
func GetRequest(ctx echo.Context) Request {
|
||||
return Request{
|
||||
Enabled: ctx.Request().Header.Get(HeaderRequest) == "true",
|
||||
Boosted: ctx.Request().Header.Get(HeaderBoosted) == "true",
|
||||
Trigger: ctx.Request().Header.Get(HeaderTrigger),
|
||||
TriggerName: ctx.Request().Header.Get(HeaderTriggerName),
|
||||
Target: ctx.Request().Header.Get(HeaderTarget),
|
||||
Prompt: ctx.Request().Header.Get(HeaderPrompt),
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies data from a Response to a server response
|
||||
func (r Response) Apply(ctx echo.Context) {
|
||||
if r.Push != "" {
|
||||
ctx.Response().Header().Set(HeaderPush, r.Push)
|
||||
}
|
||||
if r.Redirect != "" {
|
||||
ctx.Response().Header().Set(HeaderRedirect, r.Redirect)
|
||||
}
|
||||
if r.Refresh {
|
||||
ctx.Response().Header().Set(HeaderRefresh, "true")
|
||||
}
|
||||
if r.Trigger != "" {
|
||||
ctx.Response().Header().Set(HeaderTrigger, r.Trigger)
|
||||
}
|
||||
if r.TriggerAfterSwap != "" {
|
||||
ctx.Response().Header().Set(HeaderTriggerAfterSwap, r.TriggerAfterSwap)
|
||||
}
|
||||
if r.TriggerAfterSettle != "" {
|
||||
ctx.Response().Header().Set(HeaderTriggerAfterSettle, r.TriggerAfterSettle)
|
||||
}
|
||||
if r.NoContent {
|
||||
ctx.Response().Status = http.StatusNoContent
|
||||
}
|
||||
}
|
||||
64
main.go
64
main.go
|
|
@ -1,64 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"goweb/routes"
|
||||
"goweb/services"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Start a new container
|
||||
c := services.NewContainer()
|
||||
defer func() {
|
||||
if err := c.Shutdown(); err != nil {
|
||||
c.Web.Logger.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Build the router
|
||||
routes.BuildRouter(c)
|
||||
|
||||
// Start the server
|
||||
go func() {
|
||||
srv := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", c.Config.HTTP.Hostname, c.Config.HTTP.Port),
|
||||
Handler: c.Web,
|
||||
ReadTimeout: c.Config.HTTP.ReadTimeout,
|
||||
WriteTimeout: c.Config.HTTP.WriteTimeout,
|
||||
IdleTimeout: c.Config.HTTP.IdleTimeout,
|
||||
}
|
||||
|
||||
if c.Config.HTTP.TLS.Enabled {
|
||||
certs, err := tls.LoadX509KeyPair(c.Config.HTTP.TLS.Certificate, c.Config.HTTP.TLS.Key)
|
||||
if err != nil {
|
||||
c.Web.Logger.Fatalf("cannot load TLS certificate: %v", err)
|
||||
}
|
||||
|
||||
srv.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{certs},
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Web.StartServer(&srv); err != http.ErrServerClosed {
|
||||
c.Web.Logger.Fatalf("shutting down the server: v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds.
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
signal.Notify(quit, os.Kill)
|
||||
<-quit
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := c.Web.Shutdown(ctx); err != nil {
|
||||
c.Web.Logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/ent"
|
||||
"goweb/msg"
|
||||
"goweb/services"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// LoadAuthenticatedUser loads the authenticated user, if one, and stores in context
|
||||
func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
u, err := authClient.GetAuthenticatedUser(c)
|
||||
switch err.(type) {
|
||||
case *ent.NotFoundError:
|
||||
c.Logger().Warn("auth user not found")
|
||||
case services.NotAuthenticatedError:
|
||||
case nil:
|
||||
c.Set(context.AuthenticatedUserKey, u)
|
||||
c.Logger().Infof("auth user loaded in to context: %d", u.ID)
|
||||
default:
|
||||
c.Logger().Errorf("error querying for authenticated user: %v", err)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadValidPasswordToken loads a valid password token entity that matches the user and token
|
||||
// provided in path parameters
|
||||
// If the token is invalid, the user will be redirected to the forgot password route
|
||||
// This requires that the user owning the token is loaded in to context
|
||||
func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Extract the user parameter
|
||||
if c.Get(context.UserKey) == nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
usr := c.Get(context.UserKey).(*ent.User)
|
||||
|
||||
token, err := authClient.GetValidPasswordToken(c, c.Param("password_token"), usr.ID)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
c.Set(context.PasswordTokenKey, token)
|
||||
return next(c)
|
||||
case services.InvalidPasswordTokenError:
|
||||
msg.Warning(c, "The link is either invalid or has expired. Please request a new one.")
|
||||
return c.Redirect(http.StatusFound, c.Echo().Reverse("forgot_password"))
|
||||
default:
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAuthentication requires that the user be authenticated in order to proceed
|
||||
func RequireAuthentication() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if u := c.Get(context.AuthenticatedUserKey); u == nil {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireNoAuthentication requires that the user not be authenticated in order to proceed
|
||||
func RequireNoAuthentication() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if u := c.Get(context.AuthenticatedUserKey); u != nil {
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"goweb/context"
|
||||
|
||||
"github.com/eko/gocache/v2/cache"
|
||||
"github.com/eko/gocache/v2/marshaler"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// CachedPage is what is used to store a rendered Page in the cache
|
||||
type CachedPage struct {
|
||||
// URL stores the URL of the requested page
|
||||
URL string
|
||||
|
||||
// HTML stores the complete HTML of the rendered Page
|
||||
HTML []byte
|
||||
|
||||
// StatusCode stores the HTTP status code
|
||||
StatusCode int
|
||||
|
||||
// Headers stores the HTTP headers
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// ServeCachedPage attempts to load a page from the cache by matching on the complete request URL
|
||||
// If a page is cached for the requested URL, it will be served here and the request terminated.
|
||||
// Any request made by an authenticated user or that is not a GET will be skipped.
|
||||
func ServeCachedPage(ch *cache.Cache) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Skip non GET requests
|
||||
if c.Request().Method != http.MethodGet {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Skip if the user is authenticated
|
||||
if c.Get(context.AuthenticatedUserKey) != nil {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Attempt to load from cache
|
||||
res, err := marshaler.New(ch).Get(
|
||||
c.Request().Context(),
|
||||
c.Request().URL.String(),
|
||||
new(CachedPage),
|
||||
)
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
c.Logger().Info("no cached page found")
|
||||
} else {
|
||||
c.Logger().Errorf("failed getting cached page: %v", err)
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
|
||||
page, ok := res.(*CachedPage)
|
||||
if !ok {
|
||||
c.Logger().Errorf("failed casting cached page")
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Set any headers
|
||||
if page.Headers != nil {
|
||||
for k, v := range page.Headers {
|
||||
c.Response().Header().Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
c.Logger().Info("serving cached page")
|
||||
|
||||
return c.HTMLBlob(page.StatusCode, page.HTML)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CacheControl sets a Cache-Control header with a given max age
|
||||
func CacheControl(maxAge time.Duration) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
v := "no-cache, no-store"
|
||||
if maxAge > 0 {
|
||||
v = fmt.Sprintf("public, max-age=%.0f", maxAge.Seconds())
|
||||
}
|
||||
c.Response().Header().Set("Cache-Control", v)
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"goweb/tests"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/eko/gocache/v2/marshaler"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServeCachedPage(t *testing.T) {
|
||||
// Cache a page
|
||||
cp := CachedPage{
|
||||
URL: "/cache",
|
||||
HTML: []byte("html"),
|
||||
Headers: make(map[string]string),
|
||||
StatusCode: http.StatusCreated,
|
||||
}
|
||||
cp.Headers["a"] = "b"
|
||||
cp.Headers["c"] = "d"
|
||||
err := marshaler.New(c.Cache).Set(context.Background(), cp.URL, cp, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Request the URL of the cached page
|
||||
ctx, rec := tests.NewContext(c.Web, cp.URL)
|
||||
err = tests.ExecuteMiddleware(ctx, ServeCachedPage(c.Cache))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cp.StatusCode, ctx.Response().Status)
|
||||
assert.Equal(t, cp.Headers["a"], ctx.Response().Header().Get("a"))
|
||||
assert.Equal(t, cp.Headers["c"], ctx.Response().Header().Get("c"))
|
||||
assert.Equal(t, cp.HTML, rec.Body.Bytes())
|
||||
|
||||
// Login and try again
|
||||
tests.InitSession(ctx)
|
||||
err = c.Auth.Login(ctx, usr.ID)
|
||||
require.NoError(t, err)
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
|
||||
err = tests.ExecuteMiddleware(ctx, ServeCachedPage(c.Cache))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestCacheControl(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
_ = tests.ExecuteMiddleware(ctx, CacheControl(time.Second*5))
|
||||
assert.Equal(t, "public, max-age=5", ctx.Response().Header().Get("Cache-Control"))
|
||||
_ = tests.ExecuteMiddleware(ctx, CacheControl(0))
|
||||
assert.Equal(t, "no-cache, no-store", ctx.Response().Header().Get("Cache-Control"))
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// LogRequestID includes the request ID in all logs for the given request
|
||||
// This requires that middleware that includes the request ID first execute
|
||||
func LogRequestID() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
rID := c.Response().Header().Get(echo.HeaderXRequestID)
|
||||
format := `{"time":"${time_rfc3339_nano}","id":"%s","level":"${level}","prefix":"${prefix}","file":"${short_file}","line":"${line}"}`
|
||||
c.Logger().SetHeader(fmt.Sprintf(format, rID))
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"goweb/tests"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
echomw "github.com/labstack/echo/v4/middleware"
|
||||
)
|
||||
|
||||
func TestLogRequestID(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
_ = tests.ExecuteMiddleware(ctx, echomw.RequestID())
|
||||
_ = tests.ExecuteMiddleware(ctx, LogRequestID())
|
||||
|
||||
var buf bytes.Buffer
|
||||
ctx.Logger().SetOutput(&buf)
|
||||
ctx.Logger().Info("test")
|
||||
rID := ctx.Response().Header().Get(echo.HeaderXRequestID)
|
||||
assert.Contains(t, buf.String(), fmt.Sprintf(`id":"%s"`, rID))
|
||||
}
|
||||
62
pkg/context/context.go
Normal file
62
pkg/context/context.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
// AuthenticatedUserKey is the key used to store the authenticated user in context.
|
||||
AuthenticatedUserKey = "auth_user"
|
||||
|
||||
// UserKey is the key used to store a user in context.
|
||||
UserKey = "user"
|
||||
|
||||
// FormKey is the key used to store a form in context.
|
||||
FormKey = "form"
|
||||
|
||||
// PasswordTokenKey is the key used to store a password token in context.
|
||||
PasswordTokenKey = "password_token"
|
||||
|
||||
// LoggerKey is the key used to store a structured logger in context.
|
||||
LoggerKey = "logger"
|
||||
|
||||
// SessionKey is the key used to store the session data in context.
|
||||
SessionKey = "session"
|
||||
|
||||
// HTMXRequestKey is the key used to store the HTMX request data in context.
|
||||
HTMXRequestKey = "htmx"
|
||||
|
||||
// CSRFKey is the key used to store the CSRF token in context.
|
||||
CSRFKey = "csrf"
|
||||
|
||||
// ConfigKey is the key used to store the configuration in context.
|
||||
ConfigKey = "config"
|
||||
|
||||
// AdminEntityKey is the key used to store the entity being operated on in the admin panel.
|
||||
AdminEntityKey = "admin:entity"
|
||||
|
||||
// AdminEntityIDKey is the key used to store the ID of the entity being operated on in the admin panel.
|
||||
AdminEntityIDKey = "admin:entity_id"
|
||||
)
|
||||
|
||||
// IsCanceledError determines if an error is due to a context cancellation.
|
||||
func IsCanceledError(err error) bool {
|
||||
return errors.Is(err, context.Canceled)
|
||||
}
|
||||
|
||||
// Cache checks if a value of a given type exists in the Echo context for a given key and returns that, otherwise
|
||||
// it will use a callback to generate a value, which is stored in the context then returned. This allows you to
|
||||
// only generate items only once for a given request.
|
||||
func Cache[T any](ctx echo.Context, key string, gen func(echo.Context) T) T {
|
||||
if val := ctx.Get(key); val != nil {
|
||||
if v, ok := val.(T); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
val := gen(ctx)
|
||||
ctx.Set(key, val)
|
||||
return val
|
||||
}
|
||||
47
pkg/context/context_test.go
Normal file
47
pkg/context/context_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsCanceled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
assert.False(t, IsCanceledError(ctx.Err()))
|
||||
cancel()
|
||||
assert.True(t, IsCanceledError(ctx.Err()))
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Microsecond*5)
|
||||
<-ctx.Done()
|
||||
cancel()
|
||||
assert.False(t, IsCanceledError(ctx.Err()))
|
||||
|
||||
assert.False(t, IsCanceledError(errors.New("test error")))
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
ctx := echo.New().NewContext(nil, nil)
|
||||
|
||||
key := "testing"
|
||||
value := "hello"
|
||||
called := 0
|
||||
callback := func(ctx echo.Context) string {
|
||||
called++
|
||||
return value
|
||||
}
|
||||
|
||||
assert.Nil(t, ctx.Get(key))
|
||||
|
||||
got := Cache(ctx, key, callback)
|
||||
assert.Equal(t, value, got)
|
||||
assert.Equal(t, 1, called)
|
||||
|
||||
got = Cache(ctx, key, callback)
|
||||
assert.Equal(t, value, got)
|
||||
assert.Equal(t, 1, called)
|
||||
}
|
||||
55
pkg/form/form.go
Normal file
55
pkg/form/form.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
)
|
||||
|
||||
// Form represents a form that can be submitted and validated.
|
||||
type Form interface {
|
||||
// Submit marks the form as submitted, stores a pointer to it in the context, binds the request
|
||||
// values to the struct fields, and validates the input based on the struct tags.
|
||||
// Returns a validator.ValidationErrors, if the form values were not valid, or an echo.HTTPError,
|
||||
// if the request failed to process.
|
||||
Submit(c echo.Context, form any) error
|
||||
|
||||
// IsSubmitted returns true if the form was submitted.
|
||||
IsSubmitted() bool
|
||||
|
||||
// IsValid returns true if the form has no validation errors.
|
||||
IsValid() bool
|
||||
|
||||
// IsDone returns true if the form was submitted and has no validation errors.
|
||||
IsDone() bool
|
||||
|
||||
// FieldHasErrors returns true if a given struct field has validation errors.
|
||||
FieldHasErrors(fieldName string) bool
|
||||
|
||||
// SetFieldError sets a validation error message for a given struct field.
|
||||
SetFieldError(fieldName string, message string)
|
||||
|
||||
// GetFieldErrors returns the validation errors for a given struct field.
|
||||
GetFieldErrors(fieldName string) []string
|
||||
}
|
||||
|
||||
// Get gets a form from the context or initializes a new copy if one is not set.
|
||||
func Get[T any](ctx echo.Context) *T {
|
||||
if v := ctx.Get(context.FormKey); v != nil {
|
||||
if form, ok := v.(*T); ok {
|
||||
return form
|
||||
}
|
||||
}
|
||||
var v T
|
||||
return &v
|
||||
}
|
||||
|
||||
// Clear removes the form set in the context.
|
||||
func Clear(ctx echo.Context) {
|
||||
ctx.Set(context.FormKey, nil)
|
||||
}
|
||||
|
||||
// Submit submits a form.
|
||||
// See Form.Submit().
|
||||
func Submit(ctx echo.Context, form Form) error {
|
||||
return form.Submit(ctx, form)
|
||||
}
|
||||
67
pkg/form/form_test.go
Normal file
67
pkg/form/form_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type mockForm struct {
|
||||
called bool
|
||||
Submission
|
||||
}
|
||||
|
||||
func (m *mockForm) Submit(_ echo.Context, _ any) error {
|
||||
m.called = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSubmit(t *testing.T) {
|
||||
m := mockForm{}
|
||||
ctx, _ := tests.NewContext(echo.New(), "/")
|
||||
err := Submit(ctx, &m)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, m.called)
|
||||
}
|
||||
|
||||
func TestGetClear(t *testing.T) {
|
||||
e := echo.New()
|
||||
|
||||
type example struct {
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
t.Run("get empty context", func(t *testing.T) {
|
||||
// Empty context, still return a form.
|
||||
ctx, _ := tests.NewContext(e, "/")
|
||||
form := Get[example](ctx)
|
||||
assert.NotNil(t, form)
|
||||
})
|
||||
|
||||
t.Run("get non-empty context", func(t *testing.T) {
|
||||
form := example{
|
||||
Name: "test",
|
||||
}
|
||||
ctx, _ := tests.NewContext(e, "/")
|
||||
ctx.Set(context.FormKey, &form)
|
||||
|
||||
// Get again and expect the values were stored.
|
||||
got := Get[example](ctx)
|
||||
require.NotNil(t, got)
|
||||
assert.Equal(t, "test", got.Name)
|
||||
|
||||
// Attempt getting a different type to ensure there's no panic.
|
||||
ret := Get[int](ctx)
|
||||
require.NotNil(t, ret)
|
||||
|
||||
// Clear.
|
||||
Clear(ctx)
|
||||
got = Get[example](ctx)
|
||||
require.NotNil(t, got)
|
||||
assert.Empty(t, got.Name)
|
||||
})
|
||||
}
|
||||
105
pkg/form/submission.go
Normal file
105
pkg/form/submission.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Submission represents the state of the submission of a form, not including the form itself.
|
||||
// This satisfies the Form interface.
|
||||
type Submission struct {
|
||||
// isSubmitted indicates if the form has been submitted.
|
||||
isSubmitted bool
|
||||
|
||||
// errors stores a slice of error message strings keyed by form struct field name.
|
||||
errors map[string][]string
|
||||
}
|
||||
|
||||
func (f *Submission) Submit(ctx echo.Context, form any) error {
|
||||
f.isSubmitted = true
|
||||
|
||||
// Set in context so the form can later be retrieved.
|
||||
ctx.Set(context.FormKey, form)
|
||||
|
||||
// Bind the values from the incoming request to the form struct.
|
||||
if err := ctx.Bind(form); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("unable to bind form: %v", err))
|
||||
}
|
||||
|
||||
// Validate the form.
|
||||
if err := ctx.Validate(form); err != nil {
|
||||
f.setErrorMessages(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Submission) IsSubmitted() bool {
|
||||
return f.isSubmitted
|
||||
}
|
||||
|
||||
func (f *Submission) IsValid() bool {
|
||||
if f.errors == nil {
|
||||
return true
|
||||
}
|
||||
return len(f.errors) == 0
|
||||
}
|
||||
|
||||
func (f *Submission) IsDone() bool {
|
||||
return f.IsSubmitted() && f.IsValid()
|
||||
}
|
||||
|
||||
func (f *Submission) FieldHasErrors(fieldName string) bool {
|
||||
return len(f.GetFieldErrors(fieldName)) > 0
|
||||
}
|
||||
|
||||
func (f *Submission) SetFieldError(fieldName string, message string) {
|
||||
if f.errors == nil {
|
||||
f.errors = make(map[string][]string)
|
||||
}
|
||||
f.errors[fieldName] = append(f.errors[fieldName], message)
|
||||
}
|
||||
|
||||
func (f *Submission) GetFieldErrors(fieldName string) []string {
|
||||
if f.errors == nil {
|
||||
return []string{}
|
||||
}
|
||||
return f.errors[fieldName]
|
||||
}
|
||||
|
||||
// setErrorMessages sets errors messages on the submission for all fields that failed validation.
|
||||
func (f *Submission) setErrorMessages(err error) {
|
||||
// Only this is supported right now
|
||||
ves, ok := err.(validator.ValidationErrors)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ve := range ves {
|
||||
var message string
|
||||
|
||||
// Provide better error messages depending on the failed validation tag.
|
||||
// This should be expanded as you use additional tags in your validation.
|
||||
switch ve.Tag() {
|
||||
case "required":
|
||||
message = "This field is required."
|
||||
case "email":
|
||||
message = "Enter a valid email address."
|
||||
case "eqfield":
|
||||
message = "Does not match."
|
||||
case "gte":
|
||||
message = fmt.Sprintf("Must be greater than or equal to %v.", ve.Param())
|
||||
default:
|
||||
message = "Invalid value."
|
||||
}
|
||||
|
||||
// Add the error.
|
||||
f.SetFieldError(ve.Field(), message)
|
||||
}
|
||||
}
|
||||
57
pkg/form/submission_test.go
Normal file
57
pkg/form/submission_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFormSubmission(t *testing.T) {
|
||||
type formTest struct {
|
||||
Name string `form:"name" validate:"required"`
|
||||
Email string `form:"email" validate:"required,email"`
|
||||
Submission
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
e.Validator = services.NewValidator()
|
||||
|
||||
t.Run("valid request", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("email=a@a.com"))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
ctx := e.NewContext(req, httptest.NewRecorder())
|
||||
|
||||
var form formTest
|
||||
err := form.Submit(ctx, &form)
|
||||
assert.IsType(t, validator.ValidationErrors{}, err)
|
||||
|
||||
assert.Empty(t, form.Name)
|
||||
assert.Equal(t, "a@a.com", form.Email)
|
||||
assert.False(t, form.IsValid())
|
||||
assert.True(t, form.FieldHasErrors("Name"))
|
||||
assert.False(t, form.FieldHasErrors("Email"))
|
||||
require.Len(t, form.GetFieldErrors("Name"), 1)
|
||||
assert.Len(t, form.GetFieldErrors("Email"), 0)
|
||||
assert.Equal(t, "This field is required.", form.GetFieldErrors("Name")[0])
|
||||
assert.False(t, form.IsDone())
|
||||
|
||||
formInCtx := Get[formTest](ctx)
|
||||
require.NotNil(t, formInCtx)
|
||||
assert.Equal(t, form.Email, formInCtx.Email)
|
||||
})
|
||||
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("abc=abc"))
|
||||
ctx := e.NewContext(req, httptest.NewRecorder())
|
||||
var form formTest
|
||||
err := form.Submit(ctx, &form)
|
||||
assert.IsType(t, new(echo.HTTPError), err)
|
||||
})
|
||||
}
|
||||
198
pkg/handlers/admin.go
Normal file
198
pkg/handlers/admin.go
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/backlite/ui"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/admin"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/middleware"
|
||||
"github.com/mikestefanello/pagoda/pkg/msg"
|
||||
"github.com/mikestefanello/pagoda/pkg/pager"
|
||||
"github.com/mikestefanello/pagoda/pkg/redirect"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Admin struct {
|
||||
orm *ent.Client
|
||||
admin *admin.Handler
|
||||
backlite *ui.Handler
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Admin))
|
||||
}
|
||||
|
||||
func (h *Admin) Init(c *services.Container) error {
|
||||
var err error
|
||||
h.orm = c.ORM
|
||||
h.admin = admin.NewHandler(h.orm, admin.HandlerConfig{
|
||||
ItemsPerPage: 25,
|
||||
PageQueryKey: pager.QueryKey,
|
||||
TimeFormat: time.DateTime,
|
||||
})
|
||||
h.backlite, err = ui.NewHandler(ui.Config{
|
||||
DB: c.Database,
|
||||
BasePath: "/admin/tasks",
|
||||
ItemsPerPage: 25,
|
||||
ReleaseAfter: c.Config.Tasks.ReleaseAfter,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Admin) Routes(g *echo.Group) {
|
||||
ag := g.Group("/admin", middleware.RequireAdmin)
|
||||
|
||||
entities := ag.Group("/entity")
|
||||
for _, n := range admin.GetEntityTypes() {
|
||||
ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.GetName())))
|
||||
ng.GET("", h.EntityList(n)).
|
||||
Name = routenames.AdminEntityList(n.GetName())
|
||||
ng.GET("/add", h.EntityAdd(n)).
|
||||
Name = routenames.AdminEntityAdd(n.GetName())
|
||||
ng.POST("/add", h.EntityAddSubmit(n)).
|
||||
Name = routenames.AdminEntityAddSubmit(n.GetName())
|
||||
ng.GET("/:id/edit", h.EntityEdit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityEdit(n.GetName())
|
||||
ng.POST("/:id/edit", h.EntityEditSubmit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityEditSubmit(n.GetName())
|
||||
ng.GET("/:id/delete", h.EntityDelete(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityDelete(n.GetName())
|
||||
ng.POST("/:id/delete", h.EntityDeleteSubmit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityDeleteSubmit(n.GetName())
|
||||
}
|
||||
|
||||
tasks := ag.Group("/tasks")
|
||||
tasks.GET("", h.Backlite(h.backlite.Running)).Name = routenames.AdminTasks
|
||||
tasks.GET("/succeeded", h.Backlite(h.backlite.Succeeded))
|
||||
tasks.GET("/failed", h.Backlite(h.backlite.Failed))
|
||||
tasks.GET("/upcoming", h.Backlite(h.backlite.Upcoming))
|
||||
tasks.GET("/task/:id", h.Backlite(h.backlite.Task))
|
||||
tasks.GET("/completed/:id", h.Backlite(h.backlite.TaskCompleted))
|
||||
}
|
||||
|
||||
// middlewareEntityLoad is middleware to extract the entity ID and attempt to load the given entity.
|
||||
func (h *Admin) middlewareEntityLoad(n admin.EntityType) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id, err := strconv.Atoi(ctx.Param("id"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "invalid entity ID")
|
||||
}
|
||||
|
||||
entity, err := h.admin.Get(ctx, n, id)
|
||||
switch {
|
||||
case err == nil:
|
||||
ctx.Set(context.AdminEntityIDKey, id)
|
||||
ctx.Set(context.AdminEntityKey, map[string][]string(entity))
|
||||
return next(ctx)
|
||||
case ent.IsNotFound(err):
|
||||
return echo.NewHTTPError(http.StatusNotFound, "entity not found")
|
||||
default:
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityList(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
list, err := h.admin.List(ctx, n)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
return pages.AdminEntityList(ctx, n, list)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityAdd(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
return pages.AdminEntityInput(ctx, n, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityAddSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
err := h.admin.Create(ctx, n)
|
||||
if err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityAdd(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.GetName()))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityEdit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
v := ctx.Get(context.AdminEntityKey).(map[string][]string)
|
||||
return pages.AdminEntityInput(ctx, n, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityEditSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id := ctx.Get(context.AdminEntityIDKey).(int)
|
||||
err := h.admin.Update(ctx, n, id)
|
||||
if err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityEdit(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Updated %s.", n.GetName()))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityDelete(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
return pages.AdminEntityDelete(ctx, n)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityDeleteSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id := ctx.Get(context.AdminEntityIDKey).(int)
|
||||
if err := h.admin.Delete(ctx, n, id); err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityDelete(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.GetName(), id))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) Backlite(handler func(http.ResponseWriter, *http.Request) error) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if id := c.Param("id"); id != "" {
|
||||
c.Request().SetPathValue("task", id)
|
||||
}
|
||||
return handler(c.Response().Writer, c.Request())
|
||||
}
|
||||
}
|
||||
380
pkg/handlers/auth.go
Normal file
380
pkg/handlers/auth.go
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/config"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/form"
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
"github.com/mikestefanello/pagoda/pkg/middleware"
|
||||
"github.com/mikestefanello/pagoda/pkg/msg"
|
||||
"github.com/mikestefanello/pagoda/pkg/redirect"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/emails"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/forms"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Auth struct {
|
||||
config *config.Config
|
||||
auth *services.AuthClient
|
||||
mail *services.MailClient
|
||||
orm *ent.Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Auth))
|
||||
}
|
||||
|
||||
func (h *Auth) Init(c *services.Container) error {
|
||||
h.config = c.Config
|
||||
h.orm = c.ORM
|
||||
h.auth = c.Auth
|
||||
h.mail = c.Mail
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Auth) Routes(g *echo.Group) {
|
||||
g.GET("/logout", h.Logout, middleware.RequireAuthentication).Name = routenames.Logout
|
||||
g.GET("/email/verify/:token", h.VerifyEmail).Name = routenames.VerifyEmail
|
||||
|
||||
noAuth := g.Group("/user", middleware.RequireNoAuthentication)
|
||||
noAuth.GET("/login", h.LoginPage).Name = routenames.Login
|
||||
noAuth.POST("/login", h.LoginSubmit).Name = routenames.LoginSubmit
|
||||
noAuth.GET("/register", h.RegisterPage).Name = routenames.Register
|
||||
noAuth.POST("/register", h.RegisterSubmit).Name = routenames.RegisterSubmit
|
||||
noAuth.GET("/password", h.ForgotPasswordPage).Name = routenames.ForgotPassword
|
||||
noAuth.POST("/password", h.ForgotPasswordSubmit).Name = routenames.ForgotPasswordSubmit
|
||||
|
||||
resetGroup := noAuth.Group("/password/reset",
|
||||
middleware.LoadUser(h.orm),
|
||||
middleware.LoadValidPasswordToken(h.auth),
|
||||
)
|
||||
resetGroup.GET("/token/:user/:password_token/:token", h.ResetPasswordPage).Name = routenames.ResetPassword
|
||||
resetGroup.POST("/token/:user/:password_token/:token", h.ResetPasswordSubmit).Name = routenames.ResetPasswordSubmit
|
||||
}
|
||||
|
||||
func (h *Auth) ForgotPasswordPage(ctx echo.Context) error {
|
||||
return pages.ForgotPassword(ctx, form.Get[forms.ForgotPassword](ctx))
|
||||
}
|
||||
|
||||
func (h *Auth) ForgotPasswordSubmit(ctx echo.Context) error {
|
||||
var input forms.ForgotPassword
|
||||
|
||||
succeed := func() error {
|
||||
form.Clear(ctx)
|
||||
msg.Success(ctx, "An email containing a link to reset your password will be sent to this address if it exists in our system.")
|
||||
return h.ForgotPasswordPage(ctx)
|
||||
}
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.ForgotPasswordPage(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to load the user.
|
||||
u, err := h.orm.User.
|
||||
Query().
|
||||
Where(user.Email(strings.ToLower(input.Email))).
|
||||
Only(ctx.Request().Context())
|
||||
|
||||
switch err.(type) {
|
||||
case *ent.NotFoundError:
|
||||
return succeed()
|
||||
case nil:
|
||||
default:
|
||||
return fail(err, "error querying user during forgot password")
|
||||
}
|
||||
|
||||
// Generate the token.
|
||||
token, pt, err := h.auth.GeneratePasswordResetToken(ctx, u.ID)
|
||||
if err != nil {
|
||||
return fail(err, "error generating password reset token")
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Info("generated password reset token",
|
||||
"user_id", u.ID,
|
||||
)
|
||||
|
||||
// Email the user.
|
||||
url := ctx.Echo().Reverse(routenames.ResetPassword, u.ID, pt.ID, token)
|
||||
err = h.mail.
|
||||
Compose().
|
||||
To(u.Email).
|
||||
Subject("Reset your password").
|
||||
Body(fmt.Sprintf("Go here to reset your password: %s", h.config.App.Host+url)).
|
||||
Send(ctx)
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "error sending password reset email")
|
||||
}
|
||||
|
||||
return succeed()
|
||||
}
|
||||
|
||||
func (h *Auth) LoginPage(ctx echo.Context) error {
|
||||
return pages.Login(ctx, form.Get[forms.Login](ctx))
|
||||
}
|
||||
|
||||
func (h *Auth) LoginSubmit(ctx echo.Context) error {
|
||||
var input forms.Login
|
||||
|
||||
authFailed := func() error {
|
||||
input.SetFieldError("Email", "")
|
||||
input.SetFieldError("Password", "")
|
||||
msg.Error(ctx, "Invalid credentials. Please try again.")
|
||||
return h.LoginPage(ctx)
|
||||
}
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.LoginPage(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to load the user.
|
||||
u, err := h.orm.User.
|
||||
Query().
|
||||
Where(user.Email(strings.ToLower(input.Email))).
|
||||
Only(ctx.Request().Context())
|
||||
|
||||
switch err.(type) {
|
||||
case *ent.NotFoundError:
|
||||
return authFailed()
|
||||
case nil:
|
||||
default:
|
||||
return fail(err, "error querying user during login")
|
||||
}
|
||||
|
||||
// Check if the password is correct.
|
||||
err = h.auth.CheckPassword(input.Password, u.Password)
|
||||
if err != nil {
|
||||
return authFailed()
|
||||
}
|
||||
|
||||
// Log the user in.
|
||||
err = h.auth.Login(ctx, u.ID)
|
||||
if err != nil {
|
||||
return fail(err, "unable to log in user")
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Welcome back, %s. You are now logged in.", u.Name))
|
||||
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Home).
|
||||
Go()
|
||||
}
|
||||
|
||||
func (h *Auth) Logout(ctx echo.Context) error {
|
||||
if err := h.auth.Logout(ctx); err == nil {
|
||||
msg.Success(ctx, "You have been logged out successfully.")
|
||||
} else {
|
||||
msg.Error(ctx, "An error occurred. Please try again.")
|
||||
}
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Home).
|
||||
Go()
|
||||
}
|
||||
|
||||
func (h *Auth) RegisterPage(ctx echo.Context) error {
|
||||
return pages.Register(ctx, form.Get[forms.Register](ctx))
|
||||
}
|
||||
|
||||
func (h *Auth) RegisterSubmit(ctx echo.Context) error {
|
||||
var input forms.Register
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.RegisterPage(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt creating the user.
|
||||
u, err := h.orm.User.
|
||||
Create().
|
||||
SetName(input.Name).
|
||||
SetEmail(input.Email).
|
||||
SetPassword(input.Password).
|
||||
Save(ctx.Request().Context())
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
log.Ctx(ctx).Info("user created",
|
||||
"user_name", u.Name,
|
||||
"user_id", u.ID,
|
||||
)
|
||||
case *ent.ConstraintError:
|
||||
msg.Warning(ctx, "A user with this email address already exists. Please log in.")
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Login).
|
||||
Go()
|
||||
default:
|
||||
return fail(err, "unable to create user")
|
||||
}
|
||||
|
||||
// Log the user in.
|
||||
err = h.auth.Login(ctx, u.ID)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error("unable to log user in",
|
||||
"error", err,
|
||||
"user_id", u.ID,
|
||||
)
|
||||
msg.Info(ctx, "Your account has been created.")
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Login).
|
||||
Go()
|
||||
}
|
||||
|
||||
msg.Success(ctx, "Your account has been created. You are now logged in.")
|
||||
|
||||
// Send the verification email.
|
||||
h.sendVerificationEmail(ctx, u)
|
||||
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Home).
|
||||
Go()
|
||||
}
|
||||
|
||||
func (h *Auth) sendVerificationEmail(ctx echo.Context, usr *ent.User) {
|
||||
// Generate a token.
|
||||
token, err := h.auth.GenerateEmailVerificationToken(usr.Email)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error("unable to generate email verification token",
|
||||
"user_id", usr.ID,
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the email.
|
||||
err = h.mail.
|
||||
Compose().
|
||||
To(usr.Email).
|
||||
Subject("Confirm your email address").
|
||||
Component(emails.ConfirmEmailAddress(ctx, usr.Name, token)).
|
||||
Send(ctx)
|
||||
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error("unable to send email verification link",
|
||||
"user_id", usr.ID,
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
msg.Info(ctx, "An email was sent to you to verify your email address.")
|
||||
}
|
||||
|
||||
func (h *Auth) ResetPasswordPage(ctx echo.Context) error {
|
||||
return pages.ResetPassword(ctx, form.Get[forms.ResetPassword](ctx))
|
||||
}
|
||||
|
||||
func (h *Auth) ResetPasswordSubmit(ctx echo.Context) error {
|
||||
var input forms.ResetPassword
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.ResetPasswordPage(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the requesting user.
|
||||
usr := ctx.Get(context.UserKey).(*ent.User)
|
||||
|
||||
// Update the user.
|
||||
_, err = usr.
|
||||
Update().
|
||||
SetPassword(input.Password).
|
||||
Save(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "unable to update password")
|
||||
}
|
||||
|
||||
// Delete all password tokens for this user.
|
||||
err = h.auth.DeletePasswordTokens(ctx, usr.ID)
|
||||
if err != nil {
|
||||
return fail(err, "unable to delete password tokens")
|
||||
}
|
||||
|
||||
msg.Success(ctx, "Your password has been updated.")
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Login).
|
||||
Go()
|
||||
}
|
||||
|
||||
func (h *Auth) VerifyEmail(ctx echo.Context) error {
|
||||
var usr *ent.User
|
||||
|
||||
// Validate the token.
|
||||
token := ctx.Param("token")
|
||||
email, err := h.auth.ValidateEmailVerificationToken(token)
|
||||
if err != nil {
|
||||
msg.Warning(ctx, "The link is either invalid or has expired.")
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Home).
|
||||
Go()
|
||||
}
|
||||
|
||||
// Check if it matches the authenticated user.
|
||||
if u := ctx.Get(context.AuthenticatedUserKey); u != nil {
|
||||
authUser := u.(*ent.User)
|
||||
|
||||
if authUser.Email == email {
|
||||
usr = authUser
|
||||
}
|
||||
}
|
||||
|
||||
// Query to find a matching user, if needed.
|
||||
if usr == nil {
|
||||
usr, err = h.orm.User.
|
||||
Query().
|
||||
Where(user.Email(email)).
|
||||
Only(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "query failed loading email verification token user")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the user, if needed.
|
||||
if !usr.Verified {
|
||||
usr, err = usr.
|
||||
Update().
|
||||
SetVerified(true).
|
||||
Save(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "failed to set user as verified")
|
||||
}
|
||||
}
|
||||
|
||||
msg.Success(ctx, "Your email has been successfully verified.")
|
||||
return redirect.New(ctx).
|
||||
Route(routenames.Home).
|
||||
Go()
|
||||
}
|
||||
76
pkg/handlers/cache.go
Normal file
76
pkg/handlers/cache.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/form"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/forms"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
cache *services.CacheClient
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Cache))
|
||||
}
|
||||
|
||||
func (h *Cache) Init(c *services.Container) error {
|
||||
h.cache = c.Cache
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Cache) Routes(g *echo.Group) {
|
||||
g.GET("/cache", h.Page).Name = routenames.Cache
|
||||
g.POST("/cache", h.Submit).Name = routenames.CacheSubmit
|
||||
}
|
||||
|
||||
func (h *Cache) Page(ctx echo.Context) error {
|
||||
f := form.Get[forms.Cache](ctx)
|
||||
|
||||
// Fetch the value from the cache.
|
||||
value, err := h.cache.
|
||||
Get().
|
||||
Key("page_cache_example").
|
||||
Fetch(ctx.Request().Context())
|
||||
|
||||
// Store the value in the form, so it can be rendered, if found.
|
||||
switch {
|
||||
case err == nil:
|
||||
f.CurrentValue = value.(string)
|
||||
case errors.Is(err, services.ErrCacheMiss):
|
||||
default:
|
||||
return fail(err, "failed to fetch from cache")
|
||||
}
|
||||
|
||||
return pages.UpdateCache(ctx, f)
|
||||
}
|
||||
|
||||
func (h *Cache) Submit(ctx echo.Context) error {
|
||||
var input forms.Cache
|
||||
|
||||
if err := form.Submit(ctx, &input); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the cache.
|
||||
err := h.cache.
|
||||
Set().
|
||||
Key("page_cache_example").
|
||||
Data(input.Value).
|
||||
Expiration(30 * time.Minute).
|
||||
Save(ctx.Request().Context())
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "unable to set cache")
|
||||
}
|
||||
|
||||
form.Clear(ctx)
|
||||
|
||||
return h.Page(ctx)
|
||||
}
|
||||
62
pkg/handlers/contact.go
Normal file
62
pkg/handlers/contact.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/form"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/forms"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Contact struct {
|
||||
mail *services.MailClient
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Contact))
|
||||
}
|
||||
|
||||
func (h *Contact) Init(c *services.Container) error {
|
||||
h.mail = c.Mail
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Contact) Routes(g *echo.Group) {
|
||||
g.GET("/contact", h.Page).Name = routenames.Contact
|
||||
g.POST("/contact", h.Submit).Name = routenames.ContactSubmit
|
||||
}
|
||||
|
||||
func (h *Contact) Page(ctx echo.Context) error {
|
||||
return pages.ContactUs(ctx, form.Get[forms.Contact](ctx))
|
||||
}
|
||||
|
||||
func (h *Contact) Submit(ctx echo.Context) error {
|
||||
var input forms.Contact
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.Page(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
err = h.mail.
|
||||
Compose().
|
||||
To(input.Email).
|
||||
Subject("Contact form submitted").
|
||||
Body(fmt.Sprintf("The message is: %s", input.Message)).
|
||||
Send(ctx)
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "unable to send email")
|
||||
}
|
||||
|
||||
return h.Page(ctx)
|
||||
}
|
||||
43
pkg/handlers/error.go
Normal file
43
pkg/handlers/error.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Error struct{}
|
||||
|
||||
func (e *Error) Page(err error, ctx echo.Context) {
|
||||
if ctx.Response().Committed || context.IsCanceledError(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine the error status code.
|
||||
code := http.StatusInternalServerError
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
code = he.Code
|
||||
}
|
||||
|
||||
// Log the error.
|
||||
logger := log.Ctx(ctx)
|
||||
switch {
|
||||
case code >= 500:
|
||||
logger.Error(err.Error())
|
||||
case code >= 400:
|
||||
logger.Warn(err.Error())
|
||||
}
|
||||
|
||||
// Set the status code.
|
||||
ctx.Response().WriteHeader(code)
|
||||
|
||||
// Render the error page.
|
||||
if err = pages.Error(ctx, code); err != nil {
|
||||
log.Ctx(ctx).Error("failed to render error page",
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
80
pkg/handlers/files.go
Normal file
80
pkg/handlers/files.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/msg"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/models"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type Files struct {
|
||||
files afero.Fs
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Files))
|
||||
}
|
||||
|
||||
func (h *Files) Init(c *services.Container) error {
|
||||
h.files = c.Files
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Files) Routes(g *echo.Group) {
|
||||
g.GET("/files", h.Page).Name = routenames.Files
|
||||
g.POST("/files", h.Submit).Name = routenames.FilesSubmit
|
||||
}
|
||||
|
||||
func (h *Files) Page(ctx echo.Context) error {
|
||||
// Compile a list of all uploaded files to be rendered.
|
||||
info, err := afero.ReadDir(h.files, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files := make([]*models.File, 0)
|
||||
for _, file := range info {
|
||||
files = append(files, &models.File{
|
||||
Name: file.Name(),
|
||||
Size: file.Size(),
|
||||
Modified: file.ModTime().Format(time.DateTime),
|
||||
})
|
||||
}
|
||||
|
||||
return pages.UploadFile(ctx, files)
|
||||
}
|
||||
|
||||
func (h *Files) Submit(ctx echo.Context) error {
|
||||
file, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
msg.Error(ctx, "A file is required.")
|
||||
return h.Page(ctx)
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := h.files.Create(file.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err = io.Copy(dst, src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("%s was uploaded successfully.", file.Filename))
|
||||
|
||||
return h.Page(ctx)
|
||||
}
|
||||
36
pkg/handlers/handlers.go
Normal file
36
pkg/handlers/handlers.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
)
|
||||
|
||||
var handlers []Handler
|
||||
|
||||
// Handler handles one or more HTTP routes
|
||||
type Handler interface {
|
||||
// Routes allows for self-registration of HTTP routes on the router
|
||||
Routes(g *echo.Group)
|
||||
|
||||
// Init provides the service container to initialize
|
||||
Init(*services.Container) error
|
||||
}
|
||||
|
||||
// Register registers a handler
|
||||
func Register(h Handler) {
|
||||
handlers = append(handlers, h)
|
||||
}
|
||||
|
||||
// GetHandlers returns all handlers
|
||||
func GetHandlers() []Handler {
|
||||
return handlers
|
||||
}
|
||||
|
||||
// fail is a helper to fail a request by returning a 500 error and logging the error
|
||||
func fail(err error, log string) error {
|
||||
// The error handler will handle logging
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("%s: %v", log, err))
|
||||
}
|
||||
29
pkg/handlers/handlers_test.go
Normal file
29
pkg/handlers/handlers_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetSetHandlers(t *testing.T) {
|
||||
handlers = []Handler{}
|
||||
assert.Empty(t, GetHandlers())
|
||||
h := new(Pages)
|
||||
Register(h)
|
||||
got := GetHandlers()
|
||||
require.Len(t, got, 1)
|
||||
assert.Equal(t, h, got[0])
|
||||
}
|
||||
|
||||
func TestFail(t *testing.T) {
|
||||
err := fail(errors.New("err message"), "log message")
|
||||
require.IsType(t, new(echo.HTTPError), err)
|
||||
he := err.(*echo.HTTPError)
|
||||
assert.Equal(t, http.StatusInternalServerError, he.Code)
|
||||
assert.Equal(t, "log message: err message", he.Message)
|
||||
}
|
||||
55
pkg/handlers/pages.go
Normal file
55
pkg/handlers/pages.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/pager"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/models"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Pages struct{}
|
||||
|
||||
func init() {
|
||||
Register(new(Pages))
|
||||
}
|
||||
|
||||
func (h *Pages) Init(c *services.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Pages) Routes(g *echo.Group) {
|
||||
g.GET("/", h.Home).Name = routenames.Home
|
||||
g.GET("/about", h.About).Name = routenames.About
|
||||
}
|
||||
|
||||
func (h *Pages) Home(ctx echo.Context) error {
|
||||
pgr := pager.NewPager(ctx, 4)
|
||||
|
||||
return pages.Home(ctx, &models.Posts{
|
||||
Posts: h.fetchPosts(&pgr),
|
||||
Pager: pgr,
|
||||
})
|
||||
}
|
||||
|
||||
// fetchPosts is a mock example of fetching posts to illustrate how paging works.
|
||||
func (h *Pages) fetchPosts(pager *pager.Pager) []models.Post {
|
||||
pager.SetItems(20)
|
||||
posts := make([]models.Post, 20)
|
||||
|
||||
for k := range posts {
|
||||
posts[k] = models.Post{
|
||||
ID: k + 1,
|
||||
Title: fmt.Sprintf("Post example #%d", k+1),
|
||||
Body: fmt.Sprintf("Lorem ipsum example #%d ddolor sit amet, consectetur adipiscing elit. Nam elementum vulputate tristique.", k+1),
|
||||
}
|
||||
}
|
||||
return posts[pager.GetOffset() : pager.GetOffset()+pager.ItemsPerPage]
|
||||
}
|
||||
|
||||
func (h *Pages) About(ctx echo.Context) error {
|
||||
return pages.About(ctx)
|
||||
}
|
||||
|
|
@ -1,23 +1,24 @@
|
|||
package routes
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Simple example of how to test routes and their markup using the test HTTP server spun up within
|
||||
// this test package
|
||||
func TestAbout_Get(t *testing.T) {
|
||||
func TestPages__About(t *testing.T) {
|
||||
doc := request(t).
|
||||
setRoute("about").
|
||||
setRoute(routenames.About).
|
||||
get().
|
||||
assertStatusCode(http.StatusOK).
|
||||
toDoc()
|
||||
|
||||
// Goquery is an excellent package to use for testing HTML markup
|
||||
h1 := doc.Find("h1.title")
|
||||
h1 := doc.Find("h1")
|
||||
assert.Len(t, h1.Nodes, 1)
|
||||
assert.Equal(t, "About", h1.Text())
|
||||
}
|
||||
93
pkg/handlers/router.go
Normal file
93
pkg/handlers/router.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/labstack/echo/v4"
|
||||
echomw "github.com/labstack/echo/v4/middleware"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/middleware"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
files "github.com/mikestefanello/pagoda/public"
|
||||
)
|
||||
|
||||
// BuildRouter builds the router.
|
||||
func BuildRouter(c *services.Container) error {
|
||||
// Force HTTPS, if enabled.
|
||||
if c.Config.HTTP.TLS.Enabled {
|
||||
c.Web.Use(echomw.HTTPSRedirect())
|
||||
}
|
||||
|
||||
// Serve public files with cache control.
|
||||
c.Web.Group("", middleware.CacheControl(c.Config.Cache.Expiration.PublicFile)).
|
||||
Static("files", "public/files")
|
||||
|
||||
// Serve static files.
|
||||
// ui.StaticFile() should be used in ui components to append a cache key to the URL to break cache
|
||||
// after each server reboot.
|
||||
c.Web.Group(
|
||||
"",
|
||||
echomw.GzipWithConfig(echomw.GzipConfig{
|
||||
Skipper: func(c echo.Context) bool {
|
||||
for _, ext := range []string{
|
||||
".js",
|
||||
".css",
|
||||
} {
|
||||
if strings.HasSuffix(c.Request().URL.Path, ext) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
}),
|
||||
middleware.CacheControl(c.Config.Cache.Expiration.PublicFile),
|
||||
).StaticFS("static", echo.MustSubFS(files.Static, "static"))
|
||||
|
||||
// Non-static file route group.
|
||||
g := c.Web.Group("")
|
||||
|
||||
// Create a cookie store for session data.
|
||||
cookieStore := sessions.NewCookieStore([]byte(c.Config.App.EncryptionKey))
|
||||
cookieStore.Options.HttpOnly = true
|
||||
cookieStore.Options.SameSite = http.SameSiteStrictMode
|
||||
|
||||
g.Use(
|
||||
echomw.RemoveTrailingSlashWithConfig(echomw.TrailingSlashConfig{
|
||||
RedirectCode: http.StatusMovedPermanently,
|
||||
}),
|
||||
echomw.Recover(),
|
||||
echomw.Secure(),
|
||||
echomw.RequestID(),
|
||||
middleware.SetLogger(),
|
||||
middleware.LogRequest(),
|
||||
echomw.Gzip(),
|
||||
echomw.TimeoutWithConfig(echomw.TimeoutConfig{
|
||||
Timeout: c.Config.App.Timeout,
|
||||
}),
|
||||
middleware.Config(c.Config),
|
||||
middleware.Session(cookieStore),
|
||||
middleware.LoadAuthenticatedUser(c.Auth),
|
||||
echomw.CSRFWithConfig(echomw.CSRFConfig{
|
||||
TokenLookup: "form:csrf",
|
||||
CookieHTTPOnly: true,
|
||||
CookieSameSite: http.SameSiteStrictMode,
|
||||
ContextKey: context.CSRFKey,
|
||||
}),
|
||||
)
|
||||
|
||||
// Error handler.
|
||||
c.Web.HTTPErrorHandler = new(Error).Page
|
||||
|
||||
// Initialize and register all handlers.
|
||||
for _, h := range GetHandlers() {
|
||||
if err := h.Init(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.Routes(g)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
package routes
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"goweb/config"
|
||||
"goweb/services"
|
||||
"github.com/mikestefanello/pagoda/config"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -27,19 +28,22 @@ func TestMain(m *testing.M) {
|
|||
|
||||
// Start a new container
|
||||
c = services.NewContainer()
|
||||
defer func() {
|
||||
if err := c.Shutdown(); err != nil {
|
||||
c.Web.Logger.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start a test HTTP server
|
||||
BuildRouter(c)
|
||||
if err := BuildRouter(c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
srv = httptest.NewServer(c.Web)
|
||||
|
||||
// Run tests
|
||||
exitVal := m.Run()
|
||||
|
||||
// Shutdown the container and test server
|
||||
if err := c.Shutdown(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
srv.Close()
|
||||
|
||||
os.Exit(exitVal)
|
||||
}
|
||||
|
||||
|
|
@ -51,8 +55,14 @@ type httpRequest struct {
|
|||
}
|
||||
|
||||
func request(t *testing.T) *httpRequest {
|
||||
jar, err := cookiejar.New(nil)
|
||||
require.NoError(t, err)
|
||||
r := httpRequest{
|
||||
t: t,
|
||||
t: t,
|
||||
body: url.Values{},
|
||||
client: http.Client{
|
||||
Jar: jar,
|
||||
},
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
|
@ -62,7 +72,7 @@ func (h *httpRequest) setClient(client http.Client) *httpRequest {
|
|||
return h
|
||||
}
|
||||
|
||||
func (h *httpRequest) setRoute(route string, params ...interface{}) *httpRequest {
|
||||
func (h *httpRequest) setRoute(route string, params ...any) *httpRequest {
|
||||
h.route = srv.URL + c.Web.Reverse(route, params)
|
||||
return h
|
||||
}
|
||||
|
|
@ -83,6 +93,18 @@ func (h *httpRequest) get() *httpResponse {
|
|||
}
|
||||
|
||||
func (h *httpRequest) post() *httpResponse {
|
||||
// Make a get request to get the CSRF token
|
||||
doc := h.get().
|
||||
assertStatusCode(http.StatusOK).
|
||||
toDoc()
|
||||
|
||||
// Extract the CSRF and include it in the POST request body
|
||||
csrf := doc.Find(`input[name="csrf"]`).First()
|
||||
token, exists := csrf.Attr("value")
|
||||
assert.True(h.t, exists)
|
||||
h.body["csrf"] = []string{token}
|
||||
|
||||
// Make the POST requests
|
||||
resp, err := h.client.PostForm(h.route, h.body)
|
||||
require.NoError(h.t, err)
|
||||
r := httpResponse{
|
||||
|
|
@ -102,7 +124,7 @@ func (h *httpResponse) assertStatusCode(code int) *httpResponse {
|
|||
return h
|
||||
}
|
||||
|
||||
func (h *httpResponse) assertRedirect(t *testing.T, route string, params ...interface{}) *httpResponse {
|
||||
func (h *httpResponse) assertRedirect(t *testing.T, route string, params ...any) *httpResponse {
|
||||
assert.Equal(t, c.Web.Reverse(route, params), h.Header.Get("Location"))
|
||||
return h
|
||||
}
|
||||
44
pkg/handlers/search.go
Normal file
44
pkg/handlers/search.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/models"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
)
|
||||
|
||||
type Search struct{}
|
||||
|
||||
func init() {
|
||||
Register(new(Search))
|
||||
}
|
||||
|
||||
func (h *Search) Init(c *services.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Search) Routes(g *echo.Group) {
|
||||
g.GET("/search", h.Page).Name = routenames.Search
|
||||
}
|
||||
|
||||
func (h *Search) Page(ctx echo.Context) error {
|
||||
// Fake search results.
|
||||
results := make([]*models.SearchResult, 0, 5)
|
||||
if search := ctx.QueryParam("query"); search != "" {
|
||||
for i := 0; i < 5; i++ {
|
||||
title := "Lorem ipsum example ddolor sit amet"
|
||||
index := rand.Intn(len(title))
|
||||
title = title[:index] + search + title[index:]
|
||||
results = append(results, &models.SearchResult{
|
||||
Title: title,
|
||||
URL: fmt.Sprintf("https://www.%s.com", search),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return pages.SearchResults(ctx, results)
|
||||
}
|
||||
71
pkg/handlers/task.go
Normal file
71
pkg/handlers/task.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mikestefanello/backlite"
|
||||
"github.com/mikestefanello/pagoda/pkg/msg"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/forms"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui/pages"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/form"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
"github.com/mikestefanello/pagoda/pkg/tasks"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
tasks *backlite.Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(new(Task))
|
||||
}
|
||||
|
||||
func (h *Task) Init(c *services.Container) error {
|
||||
h.tasks = c.Tasks
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Task) Routes(g *echo.Group) {
|
||||
g.GET("/task", h.Page).Name = routenames.Task
|
||||
g.POST("/task", h.Submit).Name = routenames.TaskSubmit
|
||||
}
|
||||
|
||||
func (h *Task) Page(ctx echo.Context) error {
|
||||
return pages.AddTask(ctx, form.Get[forms.Task](ctx))
|
||||
}
|
||||
|
||||
func (h *Task) Submit(ctx echo.Context) error {
|
||||
var input forms.Task
|
||||
|
||||
err := form.Submit(ctx, &input)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
case validator.ValidationErrors:
|
||||
return h.Page(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert the task
|
||||
_, err = h.tasks.
|
||||
Add(tasks.ExampleTask{
|
||||
Message: input.Message,
|
||||
}).
|
||||
Wait(time.Duration(input.Delay) * time.Second).
|
||||
Save()
|
||||
|
||||
if err != nil {
|
||||
return fail(err, "unable to create a task")
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("The task has been created. Check the logs in %d seconds.", input.Delay))
|
||||
form.Clear(ctx)
|
||||
|
||||
return h.Page(ctx)
|
||||
}
|
||||
97
pkg/htmx/htmx.go
Normal file
97
pkg/htmx/htmx.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package htmx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
)
|
||||
|
||||
// Request headers: https://htmx.org/docs/#request-headers
|
||||
const (
|
||||
HeaderBoosted = "HX-Boosted"
|
||||
HeaderHistoryRestoreRequest = "HX-History-Restore-Request"
|
||||
HeaderPrompt = "HX-Prompt"
|
||||
HeaderRequest = "HX-Request"
|
||||
HeaderTarget = "HX-Target"
|
||||
HeaderTrigger = "HX-Trigger"
|
||||
HeaderTriggerName = "HX-Trigger-Name"
|
||||
)
|
||||
|
||||
// Response headers: https://htmx.org/docs/#response-headers
|
||||
const (
|
||||
HeaderPushURL = "HX-Push-Url"
|
||||
HeaderRedirect = "HX-Redirect"
|
||||
HeaderReplaceURL = "HX-Replace-Url"
|
||||
HeaderRefresh = "HX-Refresh"
|
||||
HeaderTriggerAfterSettle = "HX-Trigger-After-Settle"
|
||||
HeaderTriggerAfterSwap = "HX-Trigger-After-Swap"
|
||||
)
|
||||
|
||||
type (
|
||||
// Request contains data that HTMX provides during requests.
|
||||
Request struct {
|
||||
Enabled bool
|
||||
Boosted bool
|
||||
HistoryRestore bool
|
||||
Trigger string
|
||||
TriggerName string
|
||||
Target string
|
||||
Prompt string
|
||||
}
|
||||
|
||||
// Response contain data that the server can communicate back to HTMX.
|
||||
Response struct {
|
||||
PushURL string
|
||||
Redirect string
|
||||
Refresh bool
|
||||
ReplaceURL string
|
||||
Trigger string
|
||||
TriggerAfterSwap string
|
||||
TriggerAfterSettle string
|
||||
NoContent bool
|
||||
}
|
||||
)
|
||||
|
||||
// GetRequest extracts HTMX data from the request,
|
||||
func GetRequest(ctx echo.Context) *Request {
|
||||
return context.Cache(ctx, context.HTMXRequestKey, func(ctx echo.Context) *Request {
|
||||
return &Request{
|
||||
Enabled: ctx.Request().Header.Get(HeaderRequest) == "true",
|
||||
Boosted: ctx.Request().Header.Get(HeaderBoosted) == "true",
|
||||
Trigger: ctx.Request().Header.Get(HeaderTrigger),
|
||||
TriggerName: ctx.Request().Header.Get(HeaderTriggerName),
|
||||
Target: ctx.Request().Header.Get(HeaderTarget),
|
||||
Prompt: ctx.Request().Header.Get(HeaderPrompt),
|
||||
HistoryRestore: ctx.Request().Header.Get(HeaderHistoryRestoreRequest) == "true",
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Apply applies data from a Response to a server response.
|
||||
func (r Response) Apply(ctx echo.Context) {
|
||||
if r.PushURL != "" {
|
||||
ctx.Response().Header().Set(HeaderPushURL, r.PushURL)
|
||||
}
|
||||
if r.Redirect != "" {
|
||||
ctx.Response().Header().Set(HeaderRedirect, r.Redirect)
|
||||
}
|
||||
if r.Refresh {
|
||||
ctx.Response().Header().Set(HeaderRefresh, "true")
|
||||
}
|
||||
if r.Trigger != "" {
|
||||
ctx.Response().Header().Set(HeaderTrigger, r.Trigger)
|
||||
}
|
||||
if r.TriggerAfterSwap != "" {
|
||||
ctx.Response().Header().Set(HeaderTriggerAfterSwap, r.TriggerAfterSwap)
|
||||
}
|
||||
if r.TriggerAfterSettle != "" {
|
||||
ctx.Response().Header().Set(HeaderTriggerAfterSettle, r.TriggerAfterSettle)
|
||||
}
|
||||
if r.ReplaceURL != "" {
|
||||
ctx.Response().Header().Set(HeaderReplaceURL, r.ReplaceURL)
|
||||
}
|
||||
if r.NoContent {
|
||||
ctx.Response().Status = http.StatusNoContent
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"goweb/tests"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
|
@ -19,21 +20,29 @@ func TestSetRequest(t *testing.T) {
|
|||
ctx.Request().Header.Set(HeaderTriggerName, "b")
|
||||
ctx.Request().Header.Set(HeaderTarget, "c")
|
||||
ctx.Request().Header.Set(HeaderPrompt, "d")
|
||||
ctx.Request().Header.Set(HeaderHistoryRestoreRequest, "true")
|
||||
|
||||
r := GetRequest(ctx)
|
||||
assert.Equal(t, true, r.Enabled)
|
||||
assert.Equal(t, true, r.Boosted)
|
||||
assert.Equal(t, true, r.HistoryRestore)
|
||||
assert.Equal(t, "a", r.Trigger)
|
||||
assert.Equal(t, "b", r.TriggerName)
|
||||
assert.Equal(t, "c", r.Target)
|
||||
assert.Equal(t, "d", r.Prompt)
|
||||
|
||||
cached := context.Cache(ctx, context.HTMXRequestKey, func(ctx echo.Context) *Request {
|
||||
return nil
|
||||
})
|
||||
assert.Equal(t, r, cached)
|
||||
}
|
||||
|
||||
func TestResponse_Apply(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(echo.New(), "/")
|
||||
r := Response{
|
||||
Push: "a",
|
||||
PushURL: "a",
|
||||
Redirect: "b",
|
||||
ReplaceURL: "f",
|
||||
Refresh: true,
|
||||
Trigger: "c",
|
||||
TriggerAfterSwap: "d",
|
||||
|
|
@ -42,11 +51,12 @@ func TestResponse_Apply(t *testing.T) {
|
|||
}
|
||||
r.Apply(ctx)
|
||||
|
||||
assert.Equal(t, "a", ctx.Response().Header().Get(HeaderPush))
|
||||
assert.Equal(t, "a", ctx.Response().Header().Get(HeaderPushURL))
|
||||
assert.Equal(t, "b", ctx.Response().Header().Get(HeaderRedirect))
|
||||
assert.Equal(t, "true", ctx.Response().Header().Get(HeaderRefresh))
|
||||
assert.Equal(t, "c", ctx.Response().Header().Get(HeaderTrigger))
|
||||
assert.Equal(t, "d", ctx.Response().Header().Get(HeaderTriggerAfterSwap))
|
||||
assert.Equal(t, "e", ctx.Response().Header().Get(HeaderTriggerAfterSettle))
|
||||
assert.Equal(t, "f", ctx.Response().Header().Get(HeaderReplaceURL))
|
||||
assert.Equal(t, http.StatusNoContent, ctx.Response().Status)
|
||||
}
|
||||
27
pkg/log/log.go
Normal file
27
pkg/log/log.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
)
|
||||
|
||||
// Set sets a logger in the context.
|
||||
func Set(ctx echo.Context, logger *slog.Logger) {
|
||||
ctx.Set(context.LoggerKey, logger)
|
||||
}
|
||||
|
||||
// Ctx returns the logger stored in context, or provides the default logger if one is not present.
|
||||
func Ctx(ctx echo.Context) *slog.Logger {
|
||||
if l, ok := ctx.Get(context.LoggerKey).(*slog.Logger); ok {
|
||||
return l
|
||||
}
|
||||
|
||||
return Default()
|
||||
}
|
||||
|
||||
// Default returns the default logger.
|
||||
func Default() *slog.Logger {
|
||||
return slog.Default()
|
||||
}
|
||||
21
pkg/log/log_test.go
Normal file
21
pkg/log/log_test.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCtxSet(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(echo.New(), "/")
|
||||
logger := Ctx(ctx)
|
||||
assert.NotNil(t, logger)
|
||||
|
||||
logger = logger.With("a", "b")
|
||||
Set(ctx, logger)
|
||||
|
||||
got := Ctx(ctx)
|
||||
assert.Equal(t, got, logger)
|
||||
}
|
||||
120
pkg/middleware/auth.go
Normal file
120
pkg/middleware/auth.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
"github.com/mikestefanello/pagoda/pkg/msg"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/services"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// LoadAuthenticatedUser loads the authenticated user, if one, and stores in context.
|
||||
func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
u, err := authClient.GetAuthenticatedUser(c)
|
||||
switch err.(type) {
|
||||
case *ent.NotFoundError:
|
||||
log.Ctx(c).Warn("auth user not found")
|
||||
case services.NotAuthenticatedError:
|
||||
case nil:
|
||||
c.Set(context.AuthenticatedUserKey, u)
|
||||
default:
|
||||
return echo.NewHTTPError(
|
||||
http.StatusInternalServerError,
|
||||
fmt.Sprintf("error querying for authenticated user: %v", err),
|
||||
)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadValidPasswordToken loads a valid password token entity that matches the user and token
|
||||
// provided in path parameters
|
||||
// If the token is invalid, the user will be redirected to the forgot password route
|
||||
// This requires that the user owning the token is loaded in to context.
|
||||
func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Extract the user parameter
|
||||
if c.Get(context.UserKey) == nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
usr := c.Get(context.UserKey).(*ent.User)
|
||||
|
||||
// Extract the token ID.
|
||||
tokenID, err := strconv.Atoi(c.Param("password_token"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
}
|
||||
|
||||
// Attempt to load a valid password token.
|
||||
token, err := authClient.GetValidPasswordToken(
|
||||
c,
|
||||
usr.ID,
|
||||
tokenID,
|
||||
c.Param("token"),
|
||||
)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
c.Set(context.PasswordTokenKey, token)
|
||||
return next(c)
|
||||
case services.InvalidPasswordTokenError:
|
||||
msg.Warning(c, "The link is either invalid or has expired. Please request a new one.")
|
||||
return c.Redirect(http.StatusFound, c.Echo().Reverse(routenames.ForgotPassword))
|
||||
default:
|
||||
return echo.NewHTTPError(
|
||||
http.StatusInternalServerError,
|
||||
fmt.Sprintf("error loading password token: %v", err),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAuthentication requires that the user be authenticated in order to proceed.
|
||||
func RequireAuthentication(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if u := c.Get(context.AuthenticatedUserKey); u == nil {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireNoAuthentication requires that the user not be authenticated in order to proceed.
|
||||
func RequireNoAuthentication(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if u := c.Get(context.AuthenticatedUserKey); u != nil {
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAdmin requires that the authenticated user be an admin in order to proceed.
|
||||
func RequireAdmin(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if u := c.Get(context.AuthenticatedUserKey); u != nil {
|
||||
if user, ok := u.(*ent.User); ok {
|
||||
if user.Admin {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
goctx "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/ent"
|
||||
"goweb/tests"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ func TestRequireAuthentication(t *testing.T) {
|
|||
tests.InitSession(ctx)
|
||||
|
||||
// Not logged in
|
||||
err := tests.ExecuteMiddleware(ctx, RequireAuthentication())
|
||||
err := tests.ExecuteMiddleware(ctx, RequireAuthentication)
|
||||
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
|
||||
|
||||
// Login
|
||||
|
|
@ -49,7 +50,7 @@ func TestRequireAuthentication(t *testing.T) {
|
|||
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
|
||||
|
||||
// Logged in
|
||||
err = tests.ExecuteMiddleware(ctx, RequireAuthentication())
|
||||
err = tests.ExecuteMiddleware(ctx, RequireAuthentication)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ func TestRequireNoAuthentication(t *testing.T) {
|
|||
tests.InitSession(ctx)
|
||||
|
||||
// Not logged in
|
||||
err := tests.ExecuteMiddleware(ctx, RequireNoAuthentication())
|
||||
err := tests.ExecuteMiddleware(ctx, RequireNoAuthentication)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Login
|
||||
|
|
@ -67,7 +68,7 @@ func TestRequireNoAuthentication(t *testing.T) {
|
|||
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
|
||||
|
||||
// Logged in
|
||||
err = tests.ExecuteMiddleware(ctx, RequireNoAuthentication())
|
||||
err = tests.ExecuteMiddleware(ctx, RequireNoAuthentication)
|
||||
tests.AssertHTTPErrorCode(t, err, http.StatusForbidden)
|
||||
}
|
||||
|
||||
|
|
@ -79,17 +80,17 @@ func TestLoadValidPasswordToken(t *testing.T) {
|
|||
err := tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
|
||||
tests.AssertHTTPErrorCode(t, err, http.StatusInternalServerError)
|
||||
|
||||
// Add user context but no password token and expect a redirect
|
||||
ctx.SetParamNames("user")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID))
|
||||
// Add user and password token context but no token and expect a redirect
|
||||
ctx.SetParamNames("user", "password_token")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "1")
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
|
||||
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusFound, ctx.Response().Status)
|
||||
|
||||
// Add user context and invalid password token and expect a redirect
|
||||
ctx.SetParamNames("user", "password_token")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "faketoken")
|
||||
ctx.SetParamNames("user", "password_token", "token")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "1", "faketoken")
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
|
||||
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -100,8 +101,8 @@ func TestLoadValidPasswordToken(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Add user and valid password token
|
||||
ctx.SetParamNames("user", "password_token")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), token)
|
||||
ctx.SetParamNames("user", "password_token", "token")
|
||||
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), fmt.Sprintf("%d", pt.ID), token)
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
|
||||
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -109,3 +110,36 @@ func TestLoadValidPasswordToken(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
assert.Equal(t, pt.ID, ctxPt.ID)
|
||||
}
|
||||
|
||||
func TestRequireAdmin(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
tests.InitSession(ctx)
|
||||
|
||||
// Not logged in
|
||||
err := tests.ExecuteMiddleware(ctx, RequireAdmin)
|
||||
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
|
||||
|
||||
// Login as a non-admin
|
||||
err = c.Auth.Login(ctx, usr.ID)
|
||||
require.NoError(t, err)
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
|
||||
|
||||
// Logged in as a non-admin
|
||||
err = tests.ExecuteMiddleware(ctx, RequireAdmin)
|
||||
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
|
||||
|
||||
// Create an admin and login
|
||||
adm, err := tests.CreateUser(c.ORM)
|
||||
require.NoError(t, err)
|
||||
err = c.ORM.User.Update().
|
||||
SetAdmin(true).
|
||||
Exec(goctx.Background())
|
||||
require.NoError(t, err)
|
||||
err = c.Auth.Login(ctx, adm.ID)
|
||||
require.NoError(t, err)
|
||||
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
|
||||
|
||||
// Logged in as an admin
|
||||
err = tests.ExecuteMiddleware(ctx, RequireAdmin)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
22
pkg/middleware/cache.go
Normal file
22
pkg/middleware/cache.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// CacheControl sets a Cache-Control header with a given max age.
|
||||
func CacheControl(maxAge time.Duration) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
v := "no-cache, no-store"
|
||||
if maxAge > 0 {
|
||||
v = fmt.Sprintf("public, max-age=%.0f", maxAge.Seconds())
|
||||
}
|
||||
ctx.Response().Header().Set("Cache-Control", v)
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkg/middleware/cache_test.go
Normal file
18
pkg/middleware/cache_test.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCacheControl(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
_ = tests.ExecuteMiddleware(ctx, CacheControl(time.Second*5))
|
||||
assert.Equal(t, "public, max-age=5", ctx.Response().Header().Get("Cache-Control"))
|
||||
_ = tests.ExecuteMiddleware(ctx, CacheControl(0))
|
||||
assert.Equal(t, "no-cache, no-store", ctx.Response().Header().Get("Cache-Control"))
|
||||
}
|
||||
17
pkg/middleware/config.go
Normal file
17
pkg/middleware/config.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/config"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
)
|
||||
|
||||
// Config stores the configuration in the request so it can be accessed by the ui.
|
||||
func Config(cfg *config.Config) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
ctx.Set(context.ConfigKey, cfg)
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
22
pkg/middleware/config_test.go
Normal file
22
pkg/middleware/config_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mikestefanello/pagoda/config"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
ctx, _ := tests.NewContext(c.Web, "/")
|
||||
cfg := &config.Config{}
|
||||
err := tests.ExecuteMiddleware(ctx, Config(cfg))
|
||||
require.NoError(t, err)
|
||||
|
||||
got, ok := ctx.Get(context.ConfigKey).(*config.Config)
|
||||
require.True(t, ok)
|
||||
assert.Same(t, got, cfg)
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/ent"
|
||||
"goweb/ent/user"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/ent/user"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// LoadUser loads the user based on the ID provided as a path parameter
|
||||
// LoadUser loads the user based on the ID provided as a path parameter.
|
||||
func LoadUser(orm *ent.Client) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
|
@ -32,8 +33,10 @@ func LoadUser(orm *ent.Client) echo.MiddlewareFunc {
|
|||
case *ent.NotFoundError:
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
default:
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
return echo.NewHTTPError(
|
||||
http.StatusInternalServerError,
|
||||
fmt.Sprintf("error querying user: %v", err),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"goweb/context"
|
||||
"goweb/ent"
|
||||
"goweb/tests"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
"github.com/mikestefanello/pagoda/pkg/context"
|
||||
"github.com/mikestefanello/pagoda/pkg/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
73
pkg/middleware/log.go
Normal file
73
pkg/middleware/log.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/pkg/log"
|
||||
)
|
||||
|
||||
// SetLogger initializes a logger for the current request and stores it in the context.
|
||||
// It's recommended to have this executed after Echo's RequestID() middleware because it will add
|
||||
// the request ID to the logger so that all log messages produced from this request have the
|
||||
// request ID in it. You can modify this code to include any other fields that you want to always
|
||||
// appear.
|
||||
func SetLogger() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
// Include the request ID in the logger
|
||||
rID := ctx.Response().Header().Get(echo.HeaderXRequestID)
|
||||
logger := log.Ctx(ctx).With("request_id", rID)
|
||||
|
||||
// TODO include other fields you may want in all logs for this request
|
||||
log.Set(ctx, logger)
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogRequest logs the current request
|
||||
// Echo provides middleware similar to this, but we want to use our own logger
|
||||
func LogRequest() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) (err error) {
|
||||
req := ctx.Request()
|
||||
res := ctx.Response()
|
||||
|
||||
// Track how long the request takes to complete
|
||||
start := time.Now()
|
||||
if err = next(ctx); err != nil {
|
||||
ctx.Error(err)
|
||||
}
|
||||
stop := time.Now()
|
||||
|
||||
sub := log.Ctx(ctx).With(
|
||||
"ip", ctx.RealIP(),
|
||||
"host", req.Host,
|
||||
"referer", req.Referer(),
|
||||
"status", res.Status,
|
||||
"bytes_in", func() string {
|
||||
cl := req.Header.Get(echo.HeaderContentLength)
|
||||
if cl == "" {
|
||||
cl = "0"
|
||||
}
|
||||
return cl
|
||||
}(),
|
||||
"bytes_out", strconv.FormatInt(res.Size, 10),
|
||||
"latency", stop.Sub(start).String(),
|
||||
)
|
||||
|
||||
msg := fmt.Sprintf("%s %s", req.Method, req.URL.RequestURI())
|
||||
|
||||
if res.Status >= 500 {
|
||||
sub.Error(msg)
|
||||
} else {
|
||||
sub.Info(msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue