Рефакторинг
- добавлен gin-gonic - перетасованы пакет - добавлен первый репозиторий - handlers теперь возвращают собственную ошибку
This commit is contained in:
parent
3879c9bd24
commit
ff614cea04
@ -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
|
||||
|
||||
2
pink_fox_app/.gitignore
vendored
2
pink_fox_app/.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
out.log
|
||||
err.log
|
||||
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
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
@ -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=
|
||||
|
||||
@ -6,6 +6,11 @@ import (
|
||||
"pink_fox/src/app/cmd"
|
||||
)
|
||||
|
||||
// FIXME
|
||||
// перегрузка конфига
|
||||
// отображение html страницы
|
||||
// вынести перехват ошибок в middleware
|
||||
|
||||
func main() {
|
||||
err := cmd.Execute()
|
||||
if err != nil {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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("<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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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("<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 (
|
||||
"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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
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 (
|
||||
"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)
|
||||
}
|
||||
|
||||
@ -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