diff --git a/docker-compose.yml b/docker-compose.yml index 128814f..8fcc2ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: - ./environment:/var/environment/pink_fox - ./.storage/go:/go environment: + - TZ=Europe/Moscow - ARG1 postgres: @@ -22,6 +23,8 @@ services: environment: - POSTGRES_USER=pink_fox - POSTGRES_PASSWORD=pink_fox_pass + - TZ=Europe/Moscow + - PGTZ=Europe/Moscow volumes: - ./.storage/postgres:/var/lib/postgresql/data - ./services/postgres/data:/var/backups/postgres diff --git a/pink_fox_app/.gitignore b/pink_fox_app/.gitignore index f5fb25c..d2cfe19 100644 --- a/pink_fox_app/.gitignore +++ b/pink_fox_app/.gitignore @@ -1,3 +1,5 @@ out.log err.log pink_fox +dlv.log + diff --git a/pink_fox_app/database/links.postgres.sql b/pink_fox_app/database/links.postgres.sql new file mode 100644 index 0000000..0be15da --- /dev/null +++ b/pink_fox_app/database/links.postgres.sql @@ -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); diff --git a/pink_fox_app/dlv.log b/pink_fox_app/dlv.log deleted file mode 100644 index d8690ef..0000000 --- a/pink_fox_app/dlv.log +++ /dev/null @@ -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 diff --git a/pink_fox_app/go.mod b/pink_fox_app/go.mod index feebd49..bd2507e 100644 --- a/pink_fox_app/go.mod +++ b/pink_fox_app/go.mod @@ -1,10 +1,10 @@ module pink_fox -go 1.24.0 +go 1.24.1 require ( - github.com/bytedance/sonic v1.12.9 // indirect - github.com/bytedance/sonic/loader v0.2.3 // indirect + github.com/bytedance/sonic v1.13.1 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/cloudwego/iasm v0.2.0 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/arch v0.14.0 // indirect - golang.org/x/crypto v0.35.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/arch v0.15.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pink_fox_app/go.sum b/pink_fox_app/go.sum index b2e3c53..ef4135d 100644 --- a/pink_fox_app/go.sum +++ b/pink_fox_app/go.sum @@ -1,8 +1,8 @@ -github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ= -github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8= +github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= +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.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0= -github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +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/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 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/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 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.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= +golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +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/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pink_fox_app/main.go b/pink_fox_app/main.go index 4a0add9..15ad3ab 100644 --- a/pink_fox_app/main.go +++ b/pink_fox_app/main.go @@ -6,6 +6,11 @@ import ( "pink_fox/src/app/cmd" ) +// FIXME +// перегрузка конфига +// отображение html страницы +// вынести перехват ошибок в middleware + func main() { err := cmd.Execute() if err != nil { diff --git a/pink_fox_app/src/app/config/config.go b/pink_fox_app/src/app/config/config.go index e42d6ca..7e62f72 100644 --- a/pink_fox_app/src/app/config/config.go +++ b/pink_fox_app/src/app/config/config.go @@ -18,6 +18,7 @@ type Config struct { Port int `yaml:"port"` Db DB `yaml:"db"` LogFile string `yaml:"logFile"` + Debug bool `yaml:"debug"` } func LoadConfig(defaultConfig string, defaultPort int) (*Config, error) { @@ -38,7 +39,7 @@ func setDefaultValue(conf *Config, defaultPort int) *Config { conf.Port = defaultPort } if conf.LogFile == "" { - conf.LogFile = "/var/logger/pink_fox/app.logger" + conf.LogFile = "/var/log/pink_fox/app.log" } return conf } diff --git a/pink_fox_app/src/app/http_server/request.go b/pink_fox_app/src/app/http_server/request.go new file mode 100644 index 0000000..ce44eab --- /dev/null +++ b/pink_fox_app/src/app/http_server/request.go @@ -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) +} diff --git a/pink_fox_app/src/app/http_server/response-factory.go b/pink_fox_app/src/app/http_server/response-factory.go index 5995894..cdd9094 100644 --- a/pink_fox_app/src/app/http_server/response-factory.go +++ b/pink_fox_app/src/app/http_server/response-factory.go @@ -12,6 +12,14 @@ func NewResponseFactory(ctx *gin.Context) *ResponseFactory { return &ResponseFactory{ctx: ctx} } -func (it *ResponseFactory) String(s string) Response { +func (it *ResponseFactory) String(s string) *StringResponse { 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) +} diff --git a/pink_fox_app/src/app/http_server/response.go b/pink_fox_app/src/app/http_server/response.go index 006b9a9..b9b4078 100644 --- a/pink_fox_app/src/app/http_server/response.go +++ b/pink_fox_app/src/app/http_server/response.go @@ -1,6 +1,7 @@ package http_server import ( + "fmt" "github.com/gin-gonic/gin" "net/http" ) @@ -21,3 +22,46 @@ func NewStringResponse(s string, ctx *gin.Context) *StringResponse { func (it *StringResponse) Render() { 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("

%s

", it.msg) + } + it.ctx.Header("Content-Type", "text/html; charset=utf-8") + it.ctx.String(it.code, fmt.Sprintf("

%d %s

\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) +} diff --git a/pink_fox_app/src/app/lerror/lerror.go b/pink_fox_app/src/app/lerror/lerror.go index 4f18e1a..d790fa4 100644 --- a/pink_fox_app/src/app/lerror/lerror.go +++ b/pink_fox_app/src/app/lerror/lerror.go @@ -20,8 +20,9 @@ func init() { } type Error struct { - msg string - trace []string + msg string + trace []string + debugStack []byte } 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{ msg: err.Error(), trace: []string{trace()}, @@ -44,14 +45,29 @@ func (it *Error) Add(msg string) *Error { return it } +func (it *Error) Tap() *Error { + it.trace = append(it.trace, trace()) + return it +} + func (it *Error) String() string { result := it.msg - for _, line := range it.trace { - result += "\n" + line - } 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 { _, file, line, ok := runtime.Caller(2) if !ok { diff --git a/pink_fox_app/src/app/logger/log.go b/pink_fox_app/src/app/logger/log.go index 1297435..94b203f 100644 --- a/pink_fox_app/src/app/logger/log.go +++ b/pink_fox_app/src/app/logger/log.go @@ -28,3 +28,7 @@ func NewLogger(path string) (*Logger, error) { func (it *Logger) Close() error { return it.file.Close() } + +func (it *Logger) Error(msg string, args ...any) { + it.logger.Error(msg, args...) +} diff --git a/pink_fox_app/src/app/routes_manager/manager.go b/pink_fox_app/src/app/routes_manager/routes_manager.go similarity index 67% rename from pink_fox_app/src/app/routes_manager/manager.go rename to pink_fox_app/src/app/routes_manager/routes_manager.go index b93082e..f450404 100644 --- a/pink_fox_app/src/app/routes_manager/manager.go +++ b/pink_fox_app/src/app/routes_manager/routes_manager.go @@ -1,10 +1,15 @@ package routes_manager import ( + "fmt" "github.com/gin-gonic/gin" + "net/http" "pink_fox/src/app" "pink_fox/src/app/http_server" + le "pink_fox/src/app/lerror" "pink_fox/src/dependence" + "runtime/debug" + "strings" ) 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 { return NewGroup(it, relativePath, middlewares...) @@ -97,13 +102,25 @@ func makeHandlerGin(handler HandlerFunc, middlewares []MiddlewareFunc, applicati return func(ctx *gin.Context) { dic := dependence.NewContainer(application, ctx) 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() } } -func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) http_server.Response { - handler := func() http_server.Response { +func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) (resp http_server.Response, lerr *le.Error) { + 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) } @@ -111,11 +128,35 @@ func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence handler = createMiddleware(handler, middlewares[i], di) } - return handler() + resp, lerr = handler() + return } 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) } } + +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("
%s\n\n%s\n%s\n
", err.String(), debugStack, strings.Join(err.GetTrace(), "\n"))
+		} else {
+			message = fmt.Sprintf("
%s\n\n%s\n
", err.String(), strings.Join(err.GetTrace(), "\n"))
+		}
+	}
+	ctx.Header("Content-Type", "text/html; charset=utf-8")
+	ctx.String(500, fmt.Sprintf("

500 %s

\n%s\n", http.StatusText(500), message)) +} diff --git a/pink_fox_app/src/controllers/Go.go b/pink_fox_app/src/controllers/Go.go new file mode 100644 index 0000000..0827838 --- /dev/null +++ b/pink_fox_app/src/controllers/Go.go @@ -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 +} diff --git a/pink_fox_app/src/controllers/Index.go b/pink_fox_app/src/controllers/Index.go index 993fcc6..3dbd1a0 100644 --- a/pink_fox_app/src/controllers/Index.go +++ b/pink_fox_app/src/controllers/Index.go @@ -2,22 +2,23 @@ package controllers import ( "pink_fox/src/app/http_server" + le "pink_fox/src/app/lerror" ) type IndexController struct { responseFactory *http_server.ResponseFactory } -type IndexControllerContainer interface { +type IndexControllerDependence interface { MakeResponseFactory() *http_server.ResponseFactory } -func NewIndexController(di IndexControllerContainer) *IndexController { +func NewIndexController(depend IndexControllerDependence) *IndexController { return &IndexController{ - responseFactory: di.MakeResponseFactory(), + responseFactory: depend.MakeResponseFactory(), } } -func (it *IndexController) ActionIndex() http_server.Response { - return it.responseFactory.String("Hello world!") +func (it *IndexController) IndexAction() (http_server.Response, *le.Error) { + return it.responseFactory.String("Hello world!"), nil } diff --git a/pink_fox_app/src/dependence/container.go b/pink_fox_app/src/dependence/container.go index 4122a81..349ec0d 100644 --- a/pink_fox_app/src/dependence/container.go +++ b/pink_fox_app/src/dependence/container.go @@ -4,6 +4,7 @@ import ( "github.com/gin-gonic/gin" "pink_fox/src/app" "pink_fox/src/app/http_server" + repo "pink_fox/src/repositories" ) type Container struct { @@ -21,6 +22,14 @@ func NewContainer(application *app.Application, context *gin.Context) *Container func (it *Container) Close() { } -func (it *Container) MakeResponseFactory() *http_server.ResponseFactory { - return nil +func (it *Container) MakeRequest() *http_server.Request { + 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) } diff --git a/pink_fox_app/src/repositories/links.go b/pink_fox_app/src/repositories/links.go new file mode 100644 index 0000000..e35aeaf --- /dev/null +++ b/pink_fox_app/src/repositories/links.go @@ -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 +} diff --git a/pink_fox_app/src/routes/registration.go b/pink_fox_app/src/routes/registration.go index 5efde75..a9f302e 100644 --- a/pink_fox_app/src/routes/registration.go +++ b/pink_fox_app/src/routes/registration.go @@ -2,6 +2,7 @@ package routes import ( "pink_fox/src/app/http_server" + le "pink_fox/src/app/lerror" "pink_fox/src/app/routes_manager" "pink_fox/src/controllers" di "pink_fox/src/dependence" @@ -9,8 +10,14 @@ import ( func RegistrationRoutes(r *routes_manager.RoutesManager) { r.GET("/", IndexController) + + r.GET("/link/:token", GoController) } -func IndexController(dic *di.Container) http_server.Response { - return controllers.NewIndexController(dic).ActionIndex() +func IndexController(depend *di.Container) (http_server.Response, *le.Error) { + return controllers.NewIndexController(depend).IndexAction() +} + +func GoController(depend *di.Container) (http_server.Response, *le.Error) { + return controllers.NewGoController().IndexAction(depend) } diff --git a/services/postgres/init/01-databases.sql b/services/postgres/init/01-databases.sql index 9c10394..acfd7dd 100644 --- a/services/postgres/init/01-databases.sql +++ b/services/postgres/init/01-databases.sql @@ -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;