first commit
This commit is contained in:
parent
968575e907
commit
5d67da6b1c
32 changed files with 1319 additions and 0 deletions
80
.env-example
Normal file
80
.env-example
Normal file
|
@ -0,0 +1,80 @@
|
|||
#######################################
|
||||
###### App ######
|
||||
#######################################
|
||||
APP_NAME=GoCondor
|
||||
APP_ENV=local # local | testing | production
|
||||
APP_DEBUG_MODE=true
|
||||
App_HTTP_HOST=localhost
|
||||
App_HTTP_PORT=80
|
||||
App_USE_HTTPS=false
|
||||
App_USE_LETSENCRYPT=false
|
||||
APP_LETSENCRYPT_EMAIL=mail@example.com
|
||||
App_HTTPS_HOSTS=example.com, www.example.com
|
||||
App_REDIRECT_HTTP_TO_HTTPS=false
|
||||
App_CERT_FILE_PATH=tls/server.crt
|
||||
App_KEY_FILE_PATH=tls/server.key
|
||||
|
||||
#######################################
|
||||
###### JWT ######
|
||||
#######################################
|
||||
JWT_SECRET=dkfTgonmgaAdlgkw
|
||||
JWT_LIFESPAN_MINUTES=10080 # expires after 7 days
|
||||
|
||||
#######################################
|
||||
###### DATABASE ######
|
||||
#######################################
|
||||
DB_DRIVER=mysql # mysql | postgres | sqlite
|
||||
#_____ MYSQL _____#
|
||||
MYSQL_HOST=db-host-here
|
||||
MYSQL_DB_NAME=db-name-here
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_USERNAME=db-user-here
|
||||
MYSQL_PASSWORD=db-password-here
|
||||
MYSQL_CHARSET=utf8mb4
|
||||
|
||||
#_____ postgres _____#
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_USER=user
|
||||
POSTGRES_PASSWORD=secret
|
||||
POSTGRES_DB_NAME=db_test
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_SSL_MODE=disable
|
||||
POSTGRES_TIMEZONE=Asia/Dubai
|
||||
|
||||
#_____ SQLITE _____#
|
||||
SQLITE_DB_PATH=storage/sqlite/db.sqlite
|
||||
|
||||
#######################################
|
||||
###### CACHE ######
|
||||
#######################################
|
||||
CACHE_DRIVER=redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
REDIS_DB=0
|
||||
|
||||
#######################################
|
||||
###### Emails ######
|
||||
#######################################
|
||||
EMAILS_DRIVER=smtp # smtp | sparkpost | sendgrid | mailgun
|
||||
#_____ SMTP _____#
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=25
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_TLS_SKIP_VERIFY_HOST=true # (set true for development only!)
|
||||
|
||||
#_____ sparkpost _____#
|
||||
SPARKPOST_BASE_URL=https://api.sparkpost.com
|
||||
SPARKPOST_API_VERSION=1
|
||||
SPARKPOST_API_KEY=sparkpost-api-key-here # the api key
|
||||
|
||||
#_____ sendgrid _____#
|
||||
SENDGRID_HOST=https://api.sendgrid.com
|
||||
SENDGRID_ENDPOINT=/v3/mail/send
|
||||
SENDGRID_API_KEY=sendgrid-api-key-here # the api key
|
||||
|
||||
#_____ mailgun _____#
|
||||
MAILGUN_DOMAIN=your-domain.com # your domain
|
||||
MAILGUN_API_KEY=mailgun-api-key-here # the api key
|
||||
MAILGUN_TLS_SKIP_VERIFY_HOST=true # (set true for development only!)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Harran Ali <harran.m@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
20
README.md
20
README.md
|
@ -0,0 +1,20 @@
|
|||
## Example Todo App using Goffee framework
|
||||
This repository contains the code for an example todo app using [Goffee framework](https://git.smarteching.com/goffee)
|
||||
|
||||
### How to run locally?
|
||||
1- Clone the repository
|
||||
2- Next add your database credentials (mysql) to the `.env` file
|
||||
3- `cd` into the project dir and run `go mod tidy` to install any missing dependency
|
||||
4- Run the app using Goffee's cli tool [Goffee](https://git.smarteching.com/goffee/goffee)
|
||||
```bash
|
||||
goffee run:dev
|
||||
```
|
||||
if [Goffee](https://git.smarteching.com/goffee/goffee) is not installed you can install it by executing the following command
|
||||
```bash
|
||||
go install git.smarteching.com/goffee/goffee@latest
|
||||
|
||||
```
|
||||
|
||||
All routers are defined in the file `routes.go`
|
||||
|
||||
All request controllers are defined in the directory `controllers/`
|
20
config/cache.go
Normal file
20
config/cache.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import "git.smarteching.com/goffee/core"
|
||||
|
||||
// Retrieve the main config for the cache
|
||||
func GetCacheConfig() core.CacheConfig {
|
||||
//#####################################
|
||||
//# Main configuration for cache #####
|
||||
//#####################################
|
||||
|
||||
return core.CacheConfig{
|
||||
// For enabling and disabling the cache
|
||||
// set to true to enable it, set to false to disable
|
||||
EnableCache: true,
|
||||
}
|
||||
}
|
22
config/dotenvfile.go
Normal file
22
config/dotenvfile.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import "git.smarteching.com/goffee/core"
|
||||
|
||||
// Retrieve the main config for controlling the .env file
|
||||
func GetEnvFileConfig() core.EnvFileConfig {
|
||||
//#########################################################
|
||||
//# Main configuration for controlling the .env file #####
|
||||
//#########################################################
|
||||
|
||||
return core.EnvFileConfig{
|
||||
// Set to true to read the environment variables from the .env file and then
|
||||
// inject them into the os environment, please keep in mind this will override any
|
||||
// variables previously set in the os envrionment
|
||||
// set to false to ignore the .env file
|
||||
UseDotEnvFile: true,
|
||||
}
|
||||
}
|
20
config/gorm.go
Normal file
20
config/gorm.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import "git.smarteching.com/goffee/core"
|
||||
|
||||
// Retrieve the main config for the GORM
|
||||
func GetGormConfig() core.GormConfig {
|
||||
//#####################################
|
||||
//# Main configuration for GORM #####
|
||||
//#####################################
|
||||
|
||||
return core.GormConfig{
|
||||
// For enabling and disabling the GORM
|
||||
// set to true to enable it, set to false to disable
|
||||
EnableGorm: true,
|
||||
}
|
||||
}
|
19
config/request.go
Normal file
19
config/request.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import "git.smarteching.com/goffee/core"
|
||||
|
||||
// Retrieve the main config for the HTTP request
|
||||
func GetRequestConfig() core.RequestConfig {
|
||||
//#####################################
|
||||
//# Main configuration for gorm #####
|
||||
//#####################################
|
||||
|
||||
return core.RequestConfig{
|
||||
// Set the max file upload size
|
||||
MaxUploadFileSize: 20000000, // ~20MB
|
||||
}
|
||||
}
|
0
events/.gitkeep
Normal file
0
events/.gitkeep
Normal file
6
events/event-names.go
Normal file
6
events/event-names.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package events
|
||||
|
||||
// event names
|
||||
const USER_REGISTERED = "user-registered"
|
||||
const USER_PASSWORD_RESET_REQUESTED = "user-password-reset-requested"
|
||||
const PASSWORD_CHANGED = "password-changed"
|
0
events/eventjobs/.gitkeep
Normal file
0
events/eventjobs/.gitkeep
Normal file
31
events/eventjobs/send-password-changed-email.go
Normal file
31
events/eventjobs/send-password-changed-email.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package eventjobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
)
|
||||
|
||||
var SendPasswordChangedEmail core.EventJob = func(event *core.Event, c *core.Context) {
|
||||
go func() {
|
||||
mailer := c.GetMailer()
|
||||
logger := c.GetLogger()
|
||||
|
||||
user, ok := event.Payload["user"].(models.User)
|
||||
if !ok {
|
||||
logger.Error("[SendPasswordChangedEmail job] invalid user")
|
||||
return
|
||||
}
|
||||
mailer.SetFrom(core.EmailAddress{Name: "GoCondor", Address: "mail@example.com"})
|
||||
mailer.SetTo([]core.EmailAddress{
|
||||
{
|
||||
Name: user.Name, Address: user.Email,
|
||||
},
|
||||
})
|
||||
mailer.SetSubject("Password Changed")
|
||||
body := fmt.Sprintf("Hi %v, \nYour password have been changed. \nThanks.", user.Name)
|
||||
mailer.SetPlainTextBody(body)
|
||||
mailer.Send()
|
||||
}()
|
||||
}
|
38
events/eventjobs/send-reset-password-email.go
Normal file
38
events/eventjobs/send-reset-password-email.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package eventjobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
)
|
||||
|
||||
var SendResetPasswordEmail core.EventJob = func(event *core.Event, c *core.Context) {
|
||||
go func() {
|
||||
mailer := c.GetMailer()
|
||||
logger := c.GetLogger()
|
||||
|
||||
user, ok := event.Payload["user"].(models.User)
|
||||
if !ok {
|
||||
logger.Error("[SendResetPasswordEmail job] invalid user")
|
||||
return
|
||||
}
|
||||
mailer.SetFrom(core.EmailAddress{Name: "Goffee", Address: "mail@example.com"})
|
||||
mailer.SetTo([]core.EmailAddress{
|
||||
{
|
||||
Name: user.Name, Address: user.Email,
|
||||
},
|
||||
})
|
||||
|
||||
mailer.SetSubject("Reset Password Link")
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
}
|
||||
resetPasswordLink := fmt.Sprintf("%v/reset-password/code/%v", hostname, c.CastToString(event.Payload["code"]))
|
||||
body := fmt.Sprintf("Hi %v, <br>Click the link below to reset your password <br><a href=\"%v\">Reset Password</a>. <br>Thanks.", user.Name, resetPasswordLink)
|
||||
mailer.SetHTMLBody(body)
|
||||
mailer.Send()
|
||||
}()
|
||||
}
|
31
events/eventjobs/send-welcome-email.go
Normal file
31
events/eventjobs/send-welcome-email.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package eventjobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
)
|
||||
|
||||
var SendWelcomeEmail core.EventJob = func(event *core.Event, c *core.Context) {
|
||||
go func() {
|
||||
mailer := c.GetMailer()
|
||||
logger := c.GetLogger()
|
||||
|
||||
user, ok := event.Payload["user"].(models.User)
|
||||
if !ok {
|
||||
logger.Error("[SenEmail job] invalid user")
|
||||
return
|
||||
}
|
||||
mailer.SetFrom(core.EmailAddress{Name: "GoCondor", Address: "mail@example.com"})
|
||||
mailer.SetTo([]core.EmailAddress{
|
||||
{
|
||||
Name: user.Name, Address: user.Email,
|
||||
},
|
||||
})
|
||||
mailer.SetSubject("Welcome To GoCondor")
|
||||
body := fmt.Sprintf("Hi %v, \nWelcome to GoCondor \nYour account have been created successfully. \nThanks.", user.Name)
|
||||
mailer.SetPlainTextBody(body)
|
||||
mailer.Send()
|
||||
}()
|
||||
}
|
9
events/eventjobs/test-job.go
Normal file
9
events/eventjobs/test-job.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package eventjobs
|
||||
|
||||
import (
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
var TestEvent core.EventJob = func(event *core.Event, c *core.Context) {
|
||||
c.GetLogger().Info("hello from event test job")
|
||||
}
|
43
go.mod
Normal file
43
go.mod
Normal file
|
@ -0,0 +1,43 @@
|
|||
module git.smarteching.com/goffee/todoapp
|
||||
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
git.smarteching.com/goffee/core v1.7.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/SparkPost/gosparkpost v0.2.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
|
||||
github.com/harranali/mailing v1.2.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.3.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.10 // indirect
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.0.5 // indirect
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gorm.io/driver/mysql v1.5.1 // indirect
|
||||
gorm.io/driver/postgres v1.5.2 // indirect
|
||||
gorm.io/driver/sqlite v1.5.2 // indirect
|
||||
)
|
115
go.sum
Normal file
115
go.sum
Normal file
|
@ -0,0 +1,115 @@
|
|||
git.smarteching.com/goffee/core v1.7.2 h1:3rha+OSi1UFqHkEZwyBKuFy0pOYt60HiHpEPQFEkJlk=
|
||||
git.smarteching.com/goffee/core v1.7.2/go.mod h1:QQNIHVN6qjJBtq42WCQMrLYN9oFE3wm26SLU8ZxNTec=
|
||||
github.com/SparkPost/gosparkpost v0.2.0 h1:yzhHQT7cE+rqzd5tANNC74j+2x3lrPznqPJrxC1yR8s=
|
||||
github.com/SparkPost/gosparkpost v0.2.0/go.mod h1:S9WKcGeou7cbPpx0kTIgo8Q69WZvUmVeVzbD+djalJ4=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/brianvoe/gofakeit/v6 v6.21.0 h1:tNkm9yxEbpuPK8Bx39tT4sSc5i9SUGiciLdNix+VDQY=
|
||||
github.com/brianvoe/gofakeit/v6 v6.21.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/harranali/mailing v1.2.0 h1:ihIyJwB8hyRVcdk+v465wk1PHMrSrgJqo/kMd+gZClY=
|
||||
github.com/harranali/mailing v1.2.0/go.mod h1:4a5N3yG98pZKluMpmcYlTtll7bisvOfGQEMIng3VQk4=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
|
||||
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
||||
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jhillyerd/enmime v0.8.0/go.mod h1:MBHs3ugk03NGjMM6PuRynlKf+HA5eSillZ+TRCm73AE=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0 h1:e5LVsxpqjOYRyaOWifrJORoLQZTYDP+g4ljfmf9G2zE=
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0/go.mod h1:L9s941Lgk7iB3TgywTPz074pK2Ekkg4kgbnAaAyJ2z8=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible h1:/N2vx18Fg1KmQOh6zESc5FJB8pYwt5QFBDflYPh1KVg=
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
|
||||
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
|
||||
gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc=
|
||||
gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
390
handlers/authentication.go
Normal file
390
handlers/authentication.go
Normal file
|
@ -0,0 +1,390 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/events"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
"git.smarteching.com/goffee/todoapp/utils"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Signup(c *core.Context) *core.Response {
|
||||
name := c.GetRequestParam("name")
|
||||
email := c.GetRequestParam("email")
|
||||
password := c.GetRequestParam("password")
|
||||
// check if email exists
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal error",
|
||||
}))
|
||||
}
|
||||
if res.Error == nil {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "email already exists in the database",
|
||||
}))
|
||||
}
|
||||
|
||||
// validation data
|
||||
data := map[string]interface{}{
|
||||
"name": name,
|
||||
"email": email,
|
||||
"password": password,
|
||||
}
|
||||
// validation rules
|
||||
rules := map[string]interface{}{
|
||||
"name": "required|alphaNumeric",
|
||||
"email": "required|email",
|
||||
"password": "required|length:6,10",
|
||||
}
|
||||
// validate
|
||||
v := c.GetValidator().Validate(data, rules)
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
|
||||
//hash the password
|
||||
passwordHashed, err := c.GetHashing().HashPassword(c.CastToString(password))
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": err.Error(),
|
||||
}))
|
||||
}
|
||||
// store the record in db
|
||||
user = models.User{
|
||||
Name: c.CastToString(name),
|
||||
Email: c.CastToString(email),
|
||||
Password: passwordHashed,
|
||||
}
|
||||
res = c.GetGorm().Create(&user)
|
||||
if res.Error != nil {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": res.Error.Error(),
|
||||
}))
|
||||
}
|
||||
|
||||
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
// cache the token
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
||||
err = c.GetCache().Set(hashedCacheKey, token)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
// fire user registered event
|
||||
err = c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{
|
||||
"user": user,
|
||||
}})
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"token": token,
|
||||
}))
|
||||
}
|
||||
|
||||
func Signin(c *core.Context) *core.Response {
|
||||
email := c.GetRequestParam("email")
|
||||
password := c.GetRequestParam("password")
|
||||
|
||||
data := map[string]interface{}{
|
||||
"email": email,
|
||||
"password": password,
|
||||
}
|
||||
rules := map[string]interface{}{
|
||||
"email": "required|email",
|
||||
"password": "required",
|
||||
}
|
||||
v := c.GetValidator().Validate(data, rules)
|
||||
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
|
||||
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": err.Error(),
|
||||
}))
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
|
||||
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
// cache the token
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
||||
err = c.GetCache().Set(hashedCacheKey, token)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"token": token,
|
||||
}))
|
||||
}
|
||||
|
||||
func ResetPasswordRequest(c *core.Context) *core.Response {
|
||||
email := c.GetRequestParam("email")
|
||||
|
||||
// validation data
|
||||
data := map[string]interface{}{
|
||||
"email": email,
|
||||
}
|
||||
// validation rules
|
||||
rules := map[string]interface{}{
|
||||
"email": "required|email",
|
||||
}
|
||||
// validate
|
||||
v := c.GetValidator().Validate(data, rules)
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
|
||||
// check email in the database
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "email not found in our database",
|
||||
}))
|
||||
}
|
||||
|
||||
// generate the link
|
||||
expiresAt := time.Now().Add(time.Hour * 3).Unix()
|
||||
linkCodeData := map[string]string{
|
||||
"userID": c.CastToString(user.ID),
|
||||
"expiresAt": c.CastToString(expiresAt),
|
||||
}
|
||||
code := uuid.NewString()
|
||||
c.GetCache().SetWithExpiration(code, c.MapToJson(linkCodeData), time.Hour*3)
|
||||
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_PASSWORD_RESET_REQUESTED, Payload: map[string]interface{}{
|
||||
"user": user,
|
||||
"code": code,
|
||||
}})
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"message": "reset password email sent successfully",
|
||||
}))
|
||||
}
|
||||
|
||||
func SetNewPassword(c *core.Context) *core.Response {
|
||||
urlCode := c.CastToString(c.GetPathParam("code"))
|
||||
linkCodeDataStr, err := c.GetCache().Get(urlCode)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
var linkCode map[string]interface{}
|
||||
json.Unmarshal([]byte(linkCodeDataStr), &linkCode)
|
||||
expiresAtUnix, err := strconv.ParseInt(c.CastToString(linkCode["expiresAt"]), 10, 64)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
expiresAt := time.Unix(expiresAtUnix, 0)
|
||||
if time.Now().After(expiresAt) {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
userID, err := strconv.ParseUint(c.CastToString(linkCode["userID"]), 10, 64)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
|
||||
oldPassword := c.CastToString(c.GetRequestParam("old_password"))
|
||||
newPassword := c.CastToString(c.GetRequestParam("new_password"))
|
||||
newPasswordConfirm := c.CastToString(c.GetRequestParam("new_password_confirm"))
|
||||
|
||||
// validation data
|
||||
data := map[string]interface{}{
|
||||
"old_password": oldPassword,
|
||||
"new_password": newPassword,
|
||||
"new_password_confirm": newPasswordConfirm,
|
||||
}
|
||||
// validation rules
|
||||
rules := map[string]interface{}{
|
||||
"old_password": "required|length:6,10",
|
||||
"new_password": "required|length:6,10",
|
||||
"new_password_confirm": "required|length:6,10",
|
||||
}
|
||||
// validate
|
||||
v := c.GetValidator().Validate(data, rules)
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("id = ?", userID).First(&user)
|
||||
if res.Error != nil {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
|
||||
SamePassword, err := c.GetHashing().CheckPasswordHash(user.Password, oldPassword)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid link",
|
||||
}))
|
||||
}
|
||||
|
||||
if !SamePassword {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "the old password is incorrect",
|
||||
}))
|
||||
}
|
||||
|
||||
if newPassword != newPasswordConfirm {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "new password does not match new password confirmation",
|
||||
}))
|
||||
}
|
||||
|
||||
hashedNewPassword, err := c.GetHashing().HashPassword(newPassword)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid password",
|
||||
}))
|
||||
}
|
||||
|
||||
user.Password = hashedNewPassword
|
||||
c.GetGorm().Save(&user)
|
||||
|
||||
err = c.GetEventsManager().Fire(&core.Event{Name: events.PASSWORD_CHANGED, Payload: map[string]interface{}{
|
||||
"user": user,
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"message": "password changed successfully",
|
||||
}))
|
||||
}
|
||||
|
||||
func Signout(c *core.Context) *core.Response {
|
||||
tokenRaw := c.GetHeader("Authorization")
|
||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
if token == "" {
|
||||
return c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
}))
|
||||
}
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
return c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
}))
|
||||
}
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent)
|
||||
|
||||
err = c.GetCache().Delete(hashedCacheKey)
|
||||
if err != nil {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal error",
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.SetStatusCode(http.StatusOK).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "signed out successfully",
|
||||
}))
|
||||
}
|
21
handlers/home.go
Normal file
21
handlers/home.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
// Show home page
|
||||
func WelcomeHome(c *core.Context) *core.Response {
|
||||
message := "{\"message\": \"Welcome to GoCondor\"}"
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
|
||||
// Show dashboard
|
||||
func WelcomeToDashboard(c *core.Context) *core.Response {
|
||||
message := "{\"message\": \"Welcome to Dashboard\"}"
|
||||
return c.Response.Json(message)
|
||||
}
|
131
handlers/todos.go
Normal file
131
handlers/todos.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
)
|
||||
|
||||
func ListTodos(c *core.Context) *core.Response {
|
||||
var todos []models.Todo
|
||||
result := c.GetGorm().Find(&todos)
|
||||
if result.Error != nil {
|
||||
return c.Response.SetStatusCode(500).Json(c.MapToJson(map[string]string{"message": result.Error.Error()}))
|
||||
}
|
||||
todosJson, err := json.Marshal(todos)
|
||||
if err != nil {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": err.Error()}))
|
||||
}
|
||||
|
||||
return c.Response.Json(string(todosJson))
|
||||
}
|
||||
|
||||
func CreateTodos(c *core.Context) *core.Response {
|
||||
title := c.CastToString(c.GetRequestParam("title"))
|
||||
body := c.CastToString(c.GetRequestParam("body"))
|
||||
isDone := c.CastToString(c.GetRequestParam("isDone"))
|
||||
v := c.GetValidator().Validate(map[string]interface{}{
|
||||
"title": title,
|
||||
"body": body,
|
||||
"isDone": isDone,
|
||||
}, map[string]interface{}{
|
||||
"title": "required",
|
||||
"body": "required",
|
||||
})
|
||||
if v.Failed() {
|
||||
return c.Response.Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
result := c.GetGorm().Create(&models.Todo{
|
||||
Title: title,
|
||||
Body: body,
|
||||
IsDone: false,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return c.Response.SetStatusCode(500).Json(c.MapToJson(map[string]string{
|
||||
"message": result.Error.Error(),
|
||||
}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"message": "created successfully",
|
||||
}))
|
||||
}
|
||||
|
||||
func ShowTodo(c *core.Context) *core.Response {
|
||||
todoID := c.CastToString(c.GetPathParam("id"))
|
||||
var todo models.Todo
|
||||
result := c.GetGorm().First(&todo, todoID)
|
||||
if result.Error != nil {
|
||||
return c.Response.SetStatusCode(500).Json(c.MapToJson(map[string]string{"message": result.Error.Error()}))
|
||||
}
|
||||
todoJson, err := json.Marshal(todo)
|
||||
if err != nil {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": err.Error()}))
|
||||
}
|
||||
|
||||
return c.Response.Json(string(todoJson))
|
||||
}
|
||||
|
||||
func DeleteTodo(c *core.Context) *core.Response {
|
||||
todoID := c.CastToString(c.GetPathParam("id"))
|
||||
var todo models.Todo
|
||||
result := c.GetGorm().Delete(&todo, todoID)
|
||||
if result.Error != nil {
|
||||
return c.Response.SetStatusCode(500).Json(c.MapToJson(map[string]string{"message": result.Error.Error()}))
|
||||
}
|
||||
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": "record deleted successfully"}))
|
||||
}
|
||||
|
||||
func UpdateTodo(c *core.Context) *core.Response {
|
||||
var title string = ""
|
||||
var body string = ""
|
||||
var data map[string]interface{} = map[string]interface{}{}
|
||||
var rules map[string]interface{} = map[string]interface{}{}
|
||||
todoID := c.GetPathParam("id")
|
||||
var todo models.Todo
|
||||
result := c.GetGorm().First(&todo, todoID)
|
||||
if result.Error != nil {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": result.Error.Error()}))
|
||||
}
|
||||
if c.RequestParamExists("title") {
|
||||
title = c.CastToString(c.GetRequestParam("title"))
|
||||
data["title"] = title
|
||||
}
|
||||
if c.RequestParamExists("body") {
|
||||
body = c.CastToString(c.GetRequestParam("body"))
|
||||
data["body"] = body
|
||||
}
|
||||
if c.RequestParamExists("isDone") {
|
||||
isDoneStr := c.CastToString(c.GetRequestParam("isDone"))
|
||||
data["isDone"] = isDoneStr
|
||||
rules["isDone"] = "in:true,false"
|
||||
}
|
||||
v := c.GetValidator().Validate(data, rules)
|
||||
if v.Failed() {
|
||||
return c.Response.Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
if c.RequestParamExists("title") {
|
||||
todo.Title = title
|
||||
}
|
||||
if c.RequestParamExists("body") {
|
||||
todo.Body = body
|
||||
}
|
||||
if c.RequestParamExists("isDone") {
|
||||
isDoneStr := c.CastToString(c.GetRequestParam("isDone"))
|
||||
isDone, err := strconv.ParseBool(isDoneStr)
|
||||
if err != nil {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": err.Error()}))
|
||||
}
|
||||
todo.IsDone = isDone
|
||||
}
|
||||
c.GetGorm().Save(&todo)
|
||||
todoJson, err := json.Marshal(todo)
|
||||
if err != nil {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{"message": err.Error()}))
|
||||
}
|
||||
|
||||
return c.Response.Json(string(todoJson))
|
||||
}
|
52
main.go
Normal file
52
main.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/env"
|
||||
"git.smarteching.com/goffee/core/logger"
|
||||
"git.smarteching.com/goffee/todoapp/config"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
// The main function
|
||||
func main() {
|
||||
app := core.New()
|
||||
basePath, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal("error getting current working dir")
|
||||
}
|
||||
app.SetBasePath(basePath)
|
||||
app.MakeDirs("logs", "storage", "storage/sqlite", "tls")
|
||||
// Handle the reading of the .env file
|
||||
if config.GetEnvFileConfig().UseDotEnvFile {
|
||||
envVars, err := godotenv.Read(".env")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
env.SetEnvVars(envVars)
|
||||
}
|
||||
// Handle the logs
|
||||
app.SetLogsDriver(&logger.LogFileDriver{
|
||||
FilePath: path.Join(basePath, "logs/app.log"),
|
||||
})
|
||||
app.SetRequestConfig(config.GetRequestConfig())
|
||||
app.SetGormConfig(config.GetGormConfig())
|
||||
app.SetCacheConfig(config.GetCacheConfig())
|
||||
app.Bootstrap()
|
||||
registerGlobalMiddlewares()
|
||||
registerRoutes()
|
||||
registerEvents()
|
||||
if config.GetGormConfig().EnableGorm == true {
|
||||
RunAutoMigrations()
|
||||
}
|
||||
app.Run(httprouter.New())
|
||||
}
|
17
middlewares/another-example-middleware.go.go
Normal file
17
middlewares/another-example-middleware.go.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
// Another example middleware
|
||||
var AnotherExampleMiddleware core.Middleware = func(c *core.Context) {
|
||||
fmt.Println("another example middleware!")
|
||||
c.Next()
|
||||
}
|
69
middlewares/auth-check.go
Normal file
69
middlewares/auth-check.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
"git.smarteching.com/goffee/todoapp/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var AuthCheck core.Middleware = func(c *core.Context) {
|
||||
tokenRaw := c.GetHeader("Authorization")
|
||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
if token == "" {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent)
|
||||
|
||||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
if err != nil {
|
||||
// user signed out
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
if cachedToken != token {
|
||||
// using old token replaced with new one after recent signin
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("id = ?", payload["userID"]).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// error with the database
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal error",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// user record is not found (deleted)
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
17
middlewares/example-middleware.go
Normal file
17
middlewares/example-middleware.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
// Example middleware
|
||||
var ExampleMiddleware core.Middleware = func(c *core.Context) {
|
||||
fmt.Println("example middleware!")
|
||||
c.Next()
|
||||
}
|
16
models/todo.go
Normal file
16
models/todo.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Todo struct {
|
||||
gorm.Model
|
||||
Title string
|
||||
Body string
|
||||
IsDone bool
|
||||
// add your field here...
|
||||
}
|
||||
|
||||
// Override the table name
|
||||
func (Todo) TableName() string {
|
||||
return "todos"
|
||||
}
|
19
models/user.go
Normal file
19
models/user.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Name string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Override the table name
|
||||
func (User) TableName() string {
|
||||
return "users"
|
||||
}
|
25
register-events.go
Normal file
25
register-events.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/events"
|
||||
"git.smarteching.com/goffee/todoapp/events/eventjobs"
|
||||
)
|
||||
|
||||
// Register events
|
||||
func registerEvents() {
|
||||
eventsManager := core.ResolveEventsManager()
|
||||
//########################################
|
||||
//# events registration #####
|
||||
//########################################
|
||||
|
||||
// register your event here ...
|
||||
eventsManager.Register(events.USER_REGISTERED, eventjobs.SendWelcomeEmail)
|
||||
eventsManager.Register(events.USER_REGISTERED, eventjobs.TestEvent)
|
||||
eventsManager.Register(events.USER_PASSWORD_RESET_REQUESTED, eventjobs.SendResetPasswordEmail)
|
||||
eventsManager.Register(events.PASSWORD_CHANGED, eventjobs.SendPasswordChangedEmail)
|
||||
}
|
15
register-global-middlewares.go
Normal file
15
register-global-middlewares.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// Register middlewares globally
|
||||
func registerGlobalMiddlewares() {
|
||||
//########################################
|
||||
//# Global middlewares registration #####
|
||||
//########################################
|
||||
|
||||
// Register global middlewares here ...
|
||||
// core.UseMiddleware(middlewares.AnotherExampleMiddleware)
|
||||
}
|
33
routes.go
Normal file
33
routes.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/handlers"
|
||||
)
|
||||
|
||||
// Register the app routes
|
||||
func registerRoutes() {
|
||||
router := core.ResolveRouter()
|
||||
//#############################
|
||||
//# App Routes #####
|
||||
//#############################
|
||||
|
||||
// Define your routes here...
|
||||
router.Get("/", handlers.WelcomeHome)
|
||||
router.Get("/todos", handlers.ListTodos)
|
||||
router.Post("/todos", handlers.CreateTodos)
|
||||
router.Get("/todos/:id", handlers.ShowTodo)
|
||||
router.Delete("/todos/:id", handlers.DeleteTodo)
|
||||
router.Put("/todos/:id", handlers.UpdateTodo)
|
||||
// Uncomment the lines below to enable authentication
|
||||
// router.Post("/signup", handlers.Signup)
|
||||
// router.Post("/signin", handlers.Signin)
|
||||
// router.Post("/signout", handlers.Signout)
|
||||
// router.Post("/reset-password", handlers.ResetPasswordRequest)
|
||||
// router.Post("/reset-password/code/:code", handlers.SetNewPassword)
|
||||
// router.Get("/dashboard", handlers.WelcomeToDashboard, middlewares.AuthCheck)
|
||||
}
|
21
run-auto-migrations.go
Normal file
21
run-auto-migrations.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/todoapp/models"
|
||||
)
|
||||
|
||||
func RunAutoMigrations() {
|
||||
db := core.ResolveGorm()
|
||||
//##############################
|
||||
//# Models auto migration #####
|
||||
//##############################
|
||||
|
||||
// Add auto migrations for your models here...
|
||||
db.AutoMigrate(&models.User{})
|
||||
db.AutoMigrate(&models.Todo{})
|
||||
}
|
0
storage/.gitignore
vendored
Normal file
0
storage/.gitignore
vendored
Normal file
BIN
storage/sqlite.db
Normal file
BIN
storage/sqlite.db
Normal file
Binary file not shown.
18
utils/helpers.go
Normal file
18
utils/helpers.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// generate a hashed string to be used as key for caching auth jwt token
|
||||
func CreateAuthTokenHashedCacheKey(userID uint, userAgent string) string {
|
||||
cacheKey := fmt.Sprintf("userid:_%v_useragent:_%v_jwt_token", userID, userAgent)
|
||||
hashedCacheKey := fmt.Sprintf("%v", fmt.Sprintf("%x", md5.Sum([]byte(cacheKey))))
|
||||
|
||||
return hashedCacheKey
|
||||
}
|
Loading…
Reference in a new issue