Рефакторинг
- добавлен gin-gonic - перетасованы пакет - добавлен первый репозиторий - handlers теперь возвращают собственную ошибку
This commit is contained in:
parent
3879c9bd24
commit
ff614cea04
@ -15,6 +15,7 @@ services:
|
|||||||
- ./environment:/var/environment/pink_fox
|
- ./environment:/var/environment/pink_fox
|
||||||
- ./.storage/go:/go
|
- ./.storage/go:/go
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=Europe/Moscow
|
||||||
- ARG1
|
- ARG1
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
@ -22,6 +23,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=pink_fox
|
- POSTGRES_USER=pink_fox
|
||||||
- POSTGRES_PASSWORD=pink_fox_pass
|
- POSTGRES_PASSWORD=pink_fox_pass
|
||||||
|
- TZ=Europe/Moscow
|
||||||
|
- PGTZ=Europe/Moscow
|
||||||
volumes:
|
volumes:
|
||||||
- ./.storage/postgres:/var/lib/postgresql/data
|
- ./.storage/postgres:/var/lib/postgresql/data
|
||||||
- ./services/postgres/data:/var/backups/postgres
|
- ./services/postgres/data:/var/backups/postgres
|
||||||
|
|||||||
2
pink_fox_app/.gitignore
vendored
2
pink_fox_app/.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
out.log
|
out.log
|
||||||
err.log
|
err.log
|
||||||
pink_fox
|
pink_fox
|
||||||
|
dlv.log
|
||||||
|
|
||||||
|
|||||||
11
pink_fox_app/database/links.postgres.sql
Normal file
11
pink_fox_app/database/links.postgres.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
DROP TABLE IF EXISTS links;
|
||||||
|
|
||||||
|
CREATE TABLE links (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
token VARCHAR(24) NOT NULL UNIQUE,
|
||||||
|
url VARCHAR(512) NOT NULL,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_links_token ON links (token);
|
||||||
@ -1,2 +0,0 @@
|
|||||||
2025-03-03T16:51:40Z warning layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
|
|
||||||
не удалось открыть файл лога: open : no such file or directory
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
module pink_fox
|
module pink_fox
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.12.9 // indirect
|
github.com/bytedance/sonic v1.13.1 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
@ -27,11 +27,11 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
golang.org/x/arch v0.14.0 // indirect
|
golang.org/x/arch v0.15.0 // indirect
|
||||||
golang.org/x/crypto v0.35.0 // indirect
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.37.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
|
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
|
||||||
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
@ -67,17 +67,17 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
|
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||||
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@ -6,6 +6,11 @@ import (
|
|||||||
"pink_fox/src/app/cmd"
|
"pink_fox/src/app/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
// перегрузка конфига
|
||||||
|
// отображение html страницы
|
||||||
|
// вынести перехват ошибок в middleware
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ type Config struct {
|
|||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
Db DB `yaml:"db"`
|
Db DB `yaml:"db"`
|
||||||
LogFile string `yaml:"logFile"`
|
LogFile string `yaml:"logFile"`
|
||||||
|
Debug bool `yaml:"debug"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(defaultConfig string, defaultPort int) (*Config, error) {
|
func LoadConfig(defaultConfig string, defaultPort int) (*Config, error) {
|
||||||
@ -38,7 +39,7 @@ func setDefaultValue(conf *Config, defaultPort int) *Config {
|
|||||||
conf.Port = defaultPort
|
conf.Port = defaultPort
|
||||||
}
|
}
|
||||||
if conf.LogFile == "" {
|
if conf.LogFile == "" {
|
||||||
conf.LogFile = "/var/logger/pink_fox/app.logger"
|
conf.LogFile = "/var/log/pink_fox/app.log"
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|||||||
17
pink_fox_app/src/app/http_server/request.go
Normal file
17
pink_fox_app/src/app/http_server/request.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package http_server
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
ctx *gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequest(ctx *gin.Context) *Request {
|
||||||
|
return &Request{
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Request) Param(key string) string {
|
||||||
|
return it.ctx.Param(key)
|
||||||
|
}
|
||||||
@ -12,6 +12,14 @@ func NewResponseFactory(ctx *gin.Context) *ResponseFactory {
|
|||||||
return &ResponseFactory{ctx: ctx}
|
return &ResponseFactory{ctx: ctx}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ResponseFactory) String(s string) Response {
|
func (it *ResponseFactory) String(s string) *StringResponse {
|
||||||
return NewStringResponse(s, it.ctx)
|
return NewStringResponse(s, it.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *ResponseFactory) HtmlError(code int) *HtmlErrorResponse {
|
||||||
|
return NewHtmlErrorResponse(code, it.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *ResponseFactory) Redirect(url string) *RedirectResponse {
|
||||||
|
return NewRedirectResponse(url, it.ctx)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package http_server
|
package http_server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -21,3 +22,46 @@ func NewStringResponse(s string, ctx *gin.Context) *StringResponse {
|
|||||||
func (it *StringResponse) Render() {
|
func (it *StringResponse) Render() {
|
||||||
it.ctx.String(http.StatusOK, it.str)
|
it.ctx.String(http.StatusOK, it.str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HtmlErrorResponse struct {
|
||||||
|
code int
|
||||||
|
msg string
|
||||||
|
ctx *gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHtmlErrorResponse(code int, ctx *gin.Context) *HtmlErrorResponse {
|
||||||
|
return &HtmlErrorResponse{code: code, ctx: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *HtmlErrorResponse) Msg(msg string) *HtmlErrorResponse {
|
||||||
|
it.msg = msg
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *HtmlErrorResponse) Render() {
|
||||||
|
message := ""
|
||||||
|
if it.msg != "" {
|
||||||
|
message = fmt.Sprintf("<p>%s</p>", it.msg)
|
||||||
|
}
|
||||||
|
it.ctx.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
it.ctx.String(it.code, fmt.Sprintf("<h1>%d %s</h1>\n%s\n", it.code, http.StatusText(it.code), message))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedirectResponse struct {
|
||||||
|
code int
|
||||||
|
to string
|
||||||
|
ctx *gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedirectResponse(to string, ctx *gin.Context) *RedirectResponse {
|
||||||
|
return &RedirectResponse{code: 302, to: to, ctx: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *RedirectResponse) SetCode(code int) *RedirectResponse {
|
||||||
|
it.code = code
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *RedirectResponse) Render() {
|
||||||
|
it.ctx.Redirect(it.code, it.to)
|
||||||
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ func init() {
|
|||||||
type Error struct {
|
type Error struct {
|
||||||
msg string
|
msg string
|
||||||
trace []string
|
trace []string
|
||||||
|
debugStack []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewString(msg string) *Error {
|
func NewString(msg string) *Error {
|
||||||
@ -31,7 +32,7 @@ func NewString(msg string) *Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(err error) *Error {
|
func New(err error) *Error {
|
||||||
return &Error{
|
return &Error{
|
||||||
msg: err.Error(),
|
msg: err.Error(),
|
||||||
trace: []string{trace()},
|
trace: []string{trace()},
|
||||||
@ -44,14 +45,29 @@ func (it *Error) Add(msg string) *Error {
|
|||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *Error) Tap() *Error {
|
||||||
|
it.trace = append(it.trace, trace())
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
func (it *Error) String() string {
|
func (it *Error) String() string {
|
||||||
result := it.msg
|
result := it.msg
|
||||||
for _, line := range it.trace {
|
|
||||||
result += "\n" + line
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *Error) GetTrace() []string {
|
||||||
|
return it.trace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Error) SetDebugStack(stack []byte) *Error {
|
||||||
|
it.debugStack = stack
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Error) GetDebugStack() string {
|
||||||
|
return string(it.debugStack)
|
||||||
|
}
|
||||||
|
|
||||||
func trace() string {
|
func trace() string {
|
||||||
_, file, line, ok := runtime.Caller(2)
|
_, file, line, ok := runtime.Caller(2)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@ -28,3 +28,7 @@ func NewLogger(path string) (*Logger, error) {
|
|||||||
func (it *Logger) Close() error {
|
func (it *Logger) Close() error {
|
||||||
return it.file.Close()
|
return it.file.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *Logger) Error(msg string, args ...any) {
|
||||||
|
it.logger.Error(msg, args...)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
package routes_manager
|
package routes_manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
"pink_fox/src/app"
|
"pink_fox/src/app"
|
||||||
"pink_fox/src/app/http_server"
|
"pink_fox/src/app/http_server"
|
||||||
|
le "pink_fox/src/app/lerror"
|
||||||
"pink_fox/src/dependence"
|
"pink_fox/src/dependence"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RoutesManager struct {
|
type RoutesManager struct {
|
||||||
@ -21,11 +26,11 @@ func NewManager(e *gin.Engine, application *app.Application) *RoutesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MiddlewareFunc func(*dependence.Container, ActionFunc) http_server.Response
|
type MiddlewareFunc func(*dependence.Container, ActionFunc) (http_server.Response, *le.Error)
|
||||||
|
|
||||||
type HandlerFunc func(container *dependence.Container) http_server.Response
|
type HandlerFunc func(container *dependence.Container) (http_server.Response, *le.Error)
|
||||||
|
|
||||||
type ActionFunc func() http_server.Response
|
type ActionFunc func() (http_server.Response, *le.Error)
|
||||||
|
|
||||||
func (it *RoutesManager) Group(relativePath string, middlewares ...MiddlewareFunc) *Group {
|
func (it *RoutesManager) Group(relativePath string, middlewares ...MiddlewareFunc) *Group {
|
||||||
return NewGroup(it, relativePath, middlewares...)
|
return NewGroup(it, relativePath, middlewares...)
|
||||||
@ -97,13 +102,25 @@ func makeHandlerGin(handler HandlerFunc, middlewares []MiddlewareFunc, applicati
|
|||||||
return func(ctx *gin.Context) {
|
return func(ctx *gin.Context) {
|
||||||
dic := dependence.NewContainer(application, ctx)
|
dic := dependence.NewContainer(application, ctx)
|
||||||
defer dic.Close()
|
defer dic.Close()
|
||||||
res := pipeline(handler, middlewares, dic)
|
res, err := pipeline(handler, middlewares, dic)
|
||||||
|
if err != nil {
|
||||||
|
writeToLog(err, application)
|
||||||
|
showHtmlError(err, application, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
res.Render()
|
res.Render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) http_server.Response {
|
func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) (resp http_server.Response, lerr *le.Error) {
|
||||||
handler := func() http_server.Response {
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
resp = nil
|
||||||
|
lerr = le.New(fmt.Errorf("произошла паника: %v", err)).SetDebugStack(debug.Stack())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handler := func() (http_server.Response, *le.Error) {
|
||||||
return endpoint(di)
|
return endpoint(di)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,11 +128,35 @@ func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence
|
|||||||
handler = createMiddleware(handler, middlewares[i], di)
|
handler = createMiddleware(handler, middlewares[i], di)
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler()
|
resp, lerr = handler()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMiddleware(next ActionFunc, middleware MiddlewareFunc, di *dependence.Container) ActionFunc {
|
func createMiddleware(next ActionFunc, middleware MiddlewareFunc, di *dependence.Container) ActionFunc {
|
||||||
return func() http_server.Response {
|
return func() (http_server.Response, *le.Error) {
|
||||||
return middleware(di, next)
|
return middleware(di, next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeToLog(err *le.Error, application *app.Application) {
|
||||||
|
moreMessage := make([]any, 0)
|
||||||
|
if debugStack := err.GetDebugStack(); debugStack != "" {
|
||||||
|
moreMessage = append(moreMessage, "stack", debugStack)
|
||||||
|
}
|
||||||
|
moreMessage = append(moreMessage, "trace")
|
||||||
|
moreMessage = append(moreMessage, strings.Join(err.GetTrace(), ";\n")+";")
|
||||||
|
application.Logger.Error(err.String(), moreMessage...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showHtmlError(err *le.Error, application *app.Application, ctx *gin.Context) {
|
||||||
|
message := ""
|
||||||
|
if application.Conf.Debug {
|
||||||
|
if debugStack := err.GetDebugStack(); debugStack != "" {
|
||||||
|
message = fmt.Sprintf("<pre>%s\n\n%s\n%s\n<pre>", err.String(), debugStack, strings.Join(err.GetTrace(), "\n"))
|
||||||
|
} else {
|
||||||
|
message = fmt.Sprintf("<pre>%s\n\n%s\n<pre>", err.String(), strings.Join(err.GetTrace(), "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
ctx.String(500, fmt.Sprintf("<h1>500 %s</h1>\n%s\n", http.StatusText(500), message))
|
||||||
|
}
|
||||||
33
pink_fox_app/src/controllers/Go.go
Normal file
33
pink_fox_app/src/controllers/Go.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"pink_fox/src/app/http_server"
|
||||||
|
le "pink_fox/src/app/lerror"
|
||||||
|
"pink_fox/src/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoController struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGoController() *GoController {
|
||||||
|
return &GoController{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndexActionDependence interface {
|
||||||
|
MakeRequest() *http_server.Request
|
||||||
|
MakeResponseFactory() *http_server.ResponseFactory
|
||||||
|
MakeLinksRepository() repositories.LinksRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *GoController) IndexAction(depend IndexActionDependence) (http_server.Response, *le.Error) {
|
||||||
|
token := depend.MakeRequest().Param("token")
|
||||||
|
link, err := depend.MakeLinksRepository().ByToken(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err.Tap()
|
||||||
|
} else if link == nil {
|
||||||
|
return depend.MakeResponseFactory().HtmlError(404), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return depend.MakeResponseFactory().String(fmt.Sprintf("Ссылка %d, token \"%s\" to \"%v\", дата %v", link.ID, link.Token, link.Url, link.CreatedAt)), nil
|
||||||
|
}
|
||||||
@ -2,22 +2,23 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"pink_fox/src/app/http_server"
|
"pink_fox/src/app/http_server"
|
||||||
|
le "pink_fox/src/app/lerror"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexController struct {
|
type IndexController struct {
|
||||||
responseFactory *http_server.ResponseFactory
|
responseFactory *http_server.ResponseFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
type IndexControllerContainer interface {
|
type IndexControllerDependence interface {
|
||||||
MakeResponseFactory() *http_server.ResponseFactory
|
MakeResponseFactory() *http_server.ResponseFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIndexController(di IndexControllerContainer) *IndexController {
|
func NewIndexController(depend IndexControllerDependence) *IndexController {
|
||||||
return &IndexController{
|
return &IndexController{
|
||||||
responseFactory: di.MakeResponseFactory(),
|
responseFactory: depend.MakeResponseFactory(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *IndexController) ActionIndex() http_server.Response {
|
func (it *IndexController) IndexAction() (http_server.Response, *le.Error) {
|
||||||
return it.responseFactory.String("Hello world!")
|
return it.responseFactory.String("Hello world!"), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"pink_fox/src/app"
|
"pink_fox/src/app"
|
||||||
"pink_fox/src/app/http_server"
|
"pink_fox/src/app/http_server"
|
||||||
|
repo "pink_fox/src/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
@ -21,6 +22,14 @@ func NewContainer(application *app.Application, context *gin.Context) *Container
|
|||||||
func (it *Container) Close() {
|
func (it *Container) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Container) MakeResponseFactory() *http_server.ResponseFactory {
|
func (it *Container) MakeRequest() *http_server.Request {
|
||||||
return nil
|
return http_server.NewRequest(it.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Container) MakeResponseFactory() *http_server.ResponseFactory {
|
||||||
|
return http_server.NewResponseFactory(it.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Container) MakeLinksRepository() repo.LinksRepository {
|
||||||
|
return repo.NewLinksRepository(it.application.Conn)
|
||||||
}
|
}
|
||||||
|
|||||||
48
pink_fox_app/src/repositories/links.go
Normal file
48
pink_fox_app/src/repositories/links.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
le "pink_fox/src/app/lerror"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkData struct {
|
||||||
|
ID int
|
||||||
|
Token string
|
||||||
|
Url string
|
||||||
|
CreatedAt time.Time
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinksRepository interface {
|
||||||
|
ByToken(string) (*LinkData, *le.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinksRepositoryImpl struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinksRepository(conn *sql.DB) *LinksRepositoryImpl {
|
||||||
|
return &LinksRepositoryImpl{
|
||||||
|
db: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME нужно настроить время
|
||||||
|
// время для го
|
||||||
|
// время для postgres
|
||||||
|
|
||||||
|
func (it *LinksRepositoryImpl) ByToken(token string) (*LinkData, *le.Error) {
|
||||||
|
var link LinkData
|
||||||
|
query := `SELECT id, token, url, created_at, expires_at FROM links WHERE token = $1`
|
||||||
|
err := it.db.QueryRow(query, token).Scan(&link.ID, &link.Token, &link.Url, &link.CreatedAt, &link.ExpiresAt)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
|
return nil, le.New(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"pink_fox/src/app/http_server"
|
"pink_fox/src/app/http_server"
|
||||||
|
le "pink_fox/src/app/lerror"
|
||||||
"pink_fox/src/app/routes_manager"
|
"pink_fox/src/app/routes_manager"
|
||||||
"pink_fox/src/controllers"
|
"pink_fox/src/controllers"
|
||||||
di "pink_fox/src/dependence"
|
di "pink_fox/src/dependence"
|
||||||
@ -9,8 +10,14 @@ import (
|
|||||||
|
|
||||||
func RegistrationRoutes(r *routes_manager.RoutesManager) {
|
func RegistrationRoutes(r *routes_manager.RoutesManager) {
|
||||||
r.GET("/", IndexController)
|
r.GET("/", IndexController)
|
||||||
|
|
||||||
|
r.GET("/link/:token", GoController)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IndexController(dic *di.Container) http_server.Response {
|
func IndexController(depend *di.Container) (http_server.Response, *le.Error) {
|
||||||
return controllers.NewIndexController(dic).ActionIndex()
|
return controllers.NewIndexController(depend).IndexAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoController(depend *di.Container) (http_server.Response, *le.Error) {
|
||||||
|
return controllers.NewGoController().IndexAction(depend)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
SELECT 'CREATE DATABASE pink_fox_db with owner pink_fox' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'pink_fox_db')\gexec
|
CREATE DATABASE pink_fox_db with owner pink_fox;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user