Added migrations

This commit is contained in:
KamilM1205 2025-09-20 18:16:26 +04:00
parent 4a16acc87e
commit 5b18009658
34 changed files with 929 additions and 154 deletions

View file

@ -5,9 +5,11 @@ allow:
components:
domain: { in: internal/domain/** }
repository: {in: internal/domain/repository/**}
application: { in: internal/application/** }
commands: { in: internal/application/}
infrastructure: { in: internal/infrastructure/** }
interface: { in: internal/interface/** }
interface: { in: internal/interfaces/** }
cmd: {in: cmd/**}
commonComponents:

View file

@ -1,31 +1,36 @@
package main
// @title 58team blog backend
// @version 1.0
// @description 58team blog's backend
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.basic BasicAuth
// @title 58team blog backend
// @version 1.0
// @description 58team blog's backend
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.basic BasicAuth
import (
"58team_blog/docs"
docs "58team_blog/docs"
"58team_blog/internal/application/services"
"58team_blog/internal/infrastructure"
"58team_blog/internal/infrastructure/db"
"58team_blog/internal/infrastructure/db/repo"
"58team_blog/internal/interfaces"
"log"
"github.com/gin-gonic/gin"
"github.com/swaggo/files"
"github.com/swaggo/gin-swagger"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
func main() {
router := gin.Default()
// Swagger setup
docs.SwaggerInfo.Title = "58team blog"
docs.SwaggerInfo.Description = "This is blog 58team"
docs.SwaggerInfo.Version = "1.0"
@ -33,6 +38,33 @@ func main() {
docs.SwaggerInfo.BasePath = "/api/v1/"
docs.SwaggerInfo.Schemes = []string{"http", "https"}
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// router.GET("/swagger/*any", func(c *gin.Context) {
// path := c.Param("any")
// if strings.HasPrefix(path, "/doc.json") {
// c.File("docs/swagger.json")
// } else {
//
// }
// })
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.URL("http://localhost:8080/swag/doc/doc.json")))
router.StaticFile("/swag/doc/doc.json", "docs/swagger.json")
// Routes setup
g := router.Group("/api/v1")
config, err := infrastructure.LoadConfig()
if err != nil {
log.Fatal("Load config error: ", err)
}
d, err := db.DatabaseInit(*config)
if err != nil {
log.Fatal("Database error: ", err)
}
postRepository := repo.CreatePostRepository(d)
postService := services.CreatePostService(&postRepository)
interfaces.BindPostAdmin(&postService, g)
router.Run(":8080")
}

10
config.yaml Normal file
View file

@ -0,0 +1,10 @@
db-user: userpg
db-name: 58blog
db-password: 1205
db-host: localhost
db-port: 5432
admin_name: muts
admin_pass: 1205
images_path: ./images/
posts_path: ./posts/

View file

@ -1,35 +1,11 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
// Code generated by swaggo/swag. DO NOT EDIT.
package docs
import "github.com/swaggo/swag"
import "github.com/swaggo/swag/v2"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {},
"securityDefinitions": {
"BasicAuth": {
"type": "basic"
}
}
}`
"schemes": {{ marshal .Schemes }},"swagger":"2.0","info":{"description":"{{escape .Description}}","title":"{{.Title}}","termsOfService":"http://swagger.io/terms/","contact":{"name":"API Support","url":"http://www.swagger.io/support","email":"support@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"{{.Version}}"},"host":"{{.Host}}","basePath":"{{.BasePath}}","paths":{"/images/{path}":{"get":{"description":"get image by path","produces":["image/png","image/jpeg"],"summary":"Get an image by path","parameters":[{"type":"string","description":"Path to image","name":"path","in":"query","required":true}],"responses":{"200":{"description":"OK"}}}},"/post":{"get":{"description":"Return first 5 posts","produces":["application/json"],"tags":["post"],"summary":"Get all posts","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.GetListPostResponseItem"}}}}},"post":{"description":"Create new post in blog","consumes":["application/json"],"produces":["application/json"],"tags":["post"],"summary":"Create new post","parameters":[{"description":"Post data","name":"request","in":"body","required":true,"schema":{"$ref":"#/definitions/requests.CreatePostRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/responses.PostResponse"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/responses.ErrorResponse"}}}}},"/post/{id}":{"get":{"description":"get post by id","produces":["application/json"],"tags":["post"],"summary":"Get post by id","parameters":[{"type":"string","description":"Id of post","name":"id","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.PostResponse"}}}}},"put":{"description":"update post content","produces":["application/json"],"tags":["post"],"summary":"Update post content","parameters":[{"type":"string","description":"Id of post","name":"id","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/responses.PostResponse"}}}}},"/post/{offset}":{"get":{"description":"return 5 posts after first offset posts","produces":["application/json"],"tags":["post"],"summary":"Get posts after offset","parameters":[{"type":"integer","description":"Offset of posts","name":"offset","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.GetListPostResponseItem"}}}}}}},"definitions":{"requests.CreatePostRequest":{"type":"object","required":["content","description","title","userId"],"properties":{"content":{"type":"string","minLength":36},"description":{"type":"string","maxLength":255,"minLength":8},"title":{"type":"string","maxLength":255,"minLength":8},"userId":{"type":"string"}}},"responses.ErrorResponse":{"type":"object","properties":{"error_code":{"type":"integer"},"message":{"type":"string"}}},"responses.GetListPostResponseItem":{"type":"object","properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}}},"responses.PostResponse":{"type":"object","properties":{"content":{"type":"string"},"createdAt":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"string"},"userId":{"type":"string"}}}},"securityDefinitions":{"BasicAuth":{"type":"basic"}}}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{

View file

@ -1,26 +1 @@
{
"swagger": "2.0",
"info": {
"description": "58team blog's backend",
"title": "58team blog backend",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "localhost:8080",
"basePath": "/api/v1",
"paths": {},
"securityDefinitions": {
"BasicAuth": {
"type": "basic"
}
}
}
{"swagger":"2.0","info":{"description":"58team blog's backend","title":"58team blog backend","termsOfService":"http://swagger.io/terms/","contact":{"name":"API Support","url":"http://www.swagger.io/support","email":"support@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"1.0"},"host":"localhost:8080","basePath":"/api/v1","paths":{"/images/{path}":{"get":{"description":"get image by path","produces":["image/png","image/jpeg"],"summary":"Get an image by path","parameters":[{"type":"string","description":"Path to image","name":"path","in":"query","required":true}],"responses":{"200":{"description":"OK"}}}},"/post":{"get":{"description":"Return first 5 posts","produces":["application/json"],"tags":["post"],"summary":"Get all posts","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.GetListPostResponseItem"}}}}},"post":{"description":"Create new post in blog","consumes":["application/json"],"produces":["application/json"],"tags":["post"],"summary":"Create new post","parameters":[{"description":"Post data","name":"request","in":"body","required":true,"schema":{"$ref":"#/definitions/requests.CreatePostRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/responses.PostResponse"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/responses.ErrorResponse"}}}}},"/post/{id}":{"get":{"description":"get post by id","produces":["application/json"],"tags":["post"],"summary":"Get post by id","parameters":[{"type":"string","description":"Id of post","name":"id","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.PostResponse"}}}}},"put":{"description":"update post content","produces":["application/json"],"tags":["post"],"summary":"Update post content","parameters":[{"type":"string","description":"Id of post","name":"id","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/responses.PostResponse"}}}}},"/post/{offset}":{"get":{"description":"return 5 posts after first offset posts","produces":["application/json"],"tags":["post"],"summary":"Get posts after offset","parameters":[{"type":"integer","description":"Offset of posts","name":"offset","in":"query","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/responses.GetListPostResponseItem"}}}}}}},"definitions":{"requests.CreatePostRequest":{"type":"object","required":["content","description","title","userId"],"properties":{"content":{"type":"string","minLength":36},"description":{"type":"string","maxLength":255,"minLength":8},"title":{"type":"string","maxLength":255,"minLength":8},"userId":{"type":"string"}}},"responses.ErrorResponse":{"type":"object","properties":{"error_code":{"type":"integer"},"message":{"type":"string"}}},"responses.GetListPostResponseItem":{"type":"object","properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}}},"responses.PostResponse":{"type":"object","properties":{"content":{"type":"string"},"createdAt":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"string"},"userId":{"type":"string"}}}},"securityDefinitions":{"BasicAuth":{"type":"basic"}}}

View file

@ -1,4 +1,59 @@
basePath: /api/v1
definitions:
requests.CreatePostRequest:
properties:
content:
minLength: 36
type: string
description:
maxLength: 255
minLength: 8
type: string
title:
maxLength: 255
minLength: 8
type: string
userId:
type: string
required:
- content
- description
- title
- userId
type: object
responses.ErrorResponse:
properties:
error_code:
type: integer
message:
type: string
type: object
responses.GetListPostResponseItem:
properties:
description:
type: string
id:
type: string
title:
type: string
type: object
responses.PostResponse:
properties:
content:
type: string
createdAt:
type: string
description:
type: string
id:
type: string
title:
type: string
updatedAt:
type: string
userId:
type: string
type: object
host: localhost:8080
info:
contact:
@ -12,7 +67,123 @@ info:
termsOfService: http://swagger.io/terms/
title: 58team blog backend
version: "1.0"
paths: {}
paths:
/images/{path}:
get:
description: get image by path
parameters:
- description: Path to image
in: query
name: path
required: true
type: string
produces:
- image/png
- image/jpeg
responses:
"200":
description: OK
summary: Get an image by path
/post:
get:
description: Return first 5 posts
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/responses.GetListPostResponseItem'
type: array
summary: Get all posts
tags:
- post
post:
consumes:
- application/json
description: Create new post in blog
parameters:
- description: Post data
in: body
name: request
required: true
schema:
$ref: '#/definitions/requests.CreatePostRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/responses.PostResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/responses.ErrorResponse'
summary: Create new post
tags:
- post
/post/{id}:
get:
description: get post by id
parameters:
- description: Id of post
in: query
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/responses.PostResponse'
type: array
summary: Get post by id
tags:
- post
put:
description: update post content
parameters:
- description: Id of post
in: query
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/responses.PostResponse'
summary: Update post content
tags:
- post
/post/{offset}:
get:
description: return 5 posts after first offset posts
parameters:
- description: Offset of posts
in: query
name: offset
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/responses.GetListPostResponseItem'
type: array
summary: Get posts after offset
tags:
- post
securityDefinitions:
BasicAuth:
type: basic

106
go-arch-lint-graph.svg Executable file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 60 KiB

14
go.mod
View file

@ -16,12 +16,13 @@ require (
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-gonic/gin v1.10.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator v9.31.0+incompatible // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
@ -33,11 +34,12 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
@ -45,9 +47,11 @@ require (
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sv-tools/openapi v0.2.1 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.1 // indirect
github.com/swaggo/swag v1.16.6 // indirect
github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect

21
go.sum
View file

@ -31,17 +31,28 @@ github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
@ -65,6 +76,7 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@ -76,6 +88,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
@ -87,6 +101,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
@ -111,6 +127,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/sv-tools/openapi v0.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA=
github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
@ -119,6 +137,8 @@ github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY=
github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
@ -177,6 +197,7 @@ google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXn
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View file

@ -7,18 +7,17 @@ import (
"58team_blog/internal/application/queries"
"58team_blog/internal/domain/entities"
"58team_blog/internal/domain/repository"
"fmt"
"time"
)
type PostService struct {
repo repository.PostRepository
postsService PostsService
repo repository.PostRepository
}
func CreatePostService(repo repository.PostRepository, postsService PostsService) PostService {
func CreatePostService(repo repository.PostRepository) PostService {
return PostService{
repo: repo,
postsService: postsService,
repo: repo,
}
}
@ -30,7 +29,7 @@ func (s *PostService) Create(cmd commands.CreatePostCommand) (*common.PostResult
post, err := s.repo.Create(&entity)
if err != nil {
return nil, err
return nil, fmt.Errorf("Db error: %s", err)
}
result := mapper.CreatePostResultFromEntity(post)

View file

@ -11,12 +11,12 @@ const PostTable = "post"
type Post struct {
Id uuid.UUID `db:"id"`
UserId uuid.UUID `db:"user_id"`
UserId uuid.UUID `db:"userid"`
Title string `db:"title"`
Description string `db:"description"`
Content string `db:"content"`
CreatedAt time.Time `db:"createdAt"`
UpdatedAt time.Time `db:"updatedAt"`
CreatedAt time.Time `db:"createdat"`
UpdatedAt time.Time `db:"updatedat"`
}
func CreatePost(userId uuid.UUID, title string, description string, content string) (post Post, err error) {

View file

@ -17,10 +17,10 @@ type Config struct {
PostsPath string `mapstructure:"posts_path" default:"./posts/"`
}
func LoadConfig() (config Config, err error) {
config = Config{}
func LoadConfig() (config *Config, err error) {
config = &Config{}
if err = defaults.Set(config); err != nil {
return
return nil, err
}
viper.SetConfigName("config")
@ -30,12 +30,12 @@ func LoadConfig() (config Config, err error) {
viper.AddConfigPath("/58team_blog/cfgs/")
if err = viper.ReadInConfig(); err != nil {
return
return nil, err
}
if err = viper.Unmarshal(&config); err != nil {
return
return nil, err
}
return
return config, nil
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
type Database struct {
@ -13,7 +14,8 @@ type Database struct {
func DatabaseInit(config infrastructure.Config) (db *Database, err error) {
db = &Database{}
db_setup := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.DBUser, config.DBPass, config.DBHost, config.DBPort, config.DBName)
db_setup := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=disable", config.DBUser, config.DBPass, config.DBHost, config.DBPort, config.DBName)
db.Conn, err = sqlx.Connect("postgres", db_setup)
if err != nil {
return nil, err

View file

@ -19,8 +19,8 @@ func CreatePostRepository(conn *db.Database) PostRepository {
}
func (r *PostRepository) Create(entity *entities.Post) (*entities.Post, error) {
query := "INSERT INTO " + entities.PostTable + " (id, userId, title, description, content, createdAt, updatedAt)" +
"VALUES (:id, :userId, :title, :description, :content, :createdAt, :updatedAt)"
query := "INSERT INTO " + entities.PostTable + " (id, userid, title, description, content, createdat, updatedat)" +
"VALUES (:id, :userid, :title, :description, :content, :createdat, :updatedat)"
_, err := r.conn.Conn.NamedExec(query, entity)
return entity, err
@ -43,7 +43,7 @@ func (r *PostRepository) FindById(id uuid.UUID) (*entities.Post, error) {
func (r *PostRepository) FindAllByUserName(userName string) ([]*entities.Post, error) {
var entity_list []*entities.Post
query := "SELECT * FROM " + entities.PostTable + " WHERE userId=?"
query := "SELECT * FROM " + entities.PostTable + " WHERE userid=?"
query, args, err := sqlx.In(query, userName)
if err != nil {
@ -58,15 +58,15 @@ func (r *PostRepository) FindAllByUserName(userName string) ([]*entities.Post, e
func (r *PostRepository) GetAll() ([]*entities.Post, error) {
var entity_list []*entities.Post
query := "SELECT * FROM " + entities.PostTable
query := "SELECT * FROM " + entities.PostTable + " ORDER BY createdat, updatedat LIMIT 5;"
err := r.conn.Conn.Select(entity_list, query)
err := r.conn.Conn.Select(&entity_list, query)
return entity_list, err
}
func (r *PostRepository) Update(entity *entities.Post) error {
query := "UPDATE " + entities.PostTable + "SET title=:title, description=:description, content=:content, updatedAt=:updatedAt WHERE id=:id"
query := "UPDATE " + entities.PostTable + "SET title=:title, description=:description, content=:content, updatedat=:updatedat WHERE id=:id"
_, err := r.conn.Conn.NamedExec(query, entity)

View file

@ -0,0 +1,43 @@
package controllers
import (
"58team_blog/internal/application/services"
"github.com/gin-gonic/gin"
)
type ImagesController struct {
service *services.ImagesService
}
func CreateImagesController(service *services.ImagesService) ImagesController {
return ImagesController{
service: service,
}
}
// get /images/{path}
// post /images
// delete /images/{id}
// @Summary Get an image by path
// @Description get image by path
// @Param path query string true "Path to image"
// @Produce image/png
// @Produce image/jpeg
// @Success 200
// @Router /images/{path} [get]
func (r *ImagesController) GetImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
func (r *ImagesController) PostImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
func (r *ImagesController) DeleteImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}

View file

@ -0,0 +1,182 @@
package controllers
import (
"58team_blog/internal/application/commands"
"58team_blog/internal/application/queries"
"58team_blog/internal/application/services"
"58team_blog/internal/interfaces/api/dto"
"58team_blog/internal/interfaces/api/requests"
"58team_blog/internal/interfaces/api/responses"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type PostController struct {
service *services.PostService
}
func CreatePostController(service *services.PostService) PostController {
return PostController{
service: service,
}
}
// PostPost godoc
//
// @Summary Create new post
// @Description Create new post in blog
// @Tags post
// @Accept json
// @Produce json
// @Param request body requests.CreatePostRequest true "Post data"
// @Success 200 {object} responses.PostResponse
// @Failure 400 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /post [post]
func (r *PostController) Post(c *gin.Context) {
var request requests.CreatePostRequest
if err := c.BindJSON(&request); err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, "BadRequest")
c.IndentedJSON(resp.ErrorCode, resp)
return
}
userId, err := uuid.Parse(request.UserId)
if err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, "Incorrect user id")
c.IndentedJSON(resp.ErrorCode, resp)
return
}
cmd := commands.CreatePostCommand{
UserId: userId,
Title: request.Title,
Description: request.Description,
Content: request.Content,
}
res, err := r.service.Create(cmd)
if err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Internal server error")
c.IndentedJSON(resp.ErrorCode, resp)
return
}
response := dto.ResponseFromPostResult(res)
c.IndentedJSON(http.StatusOK, response)
}
// GetAllPost godoc
//
// @Summary Get all posts
// @Description Return first 5 posts
// @Tags post
// @Produce json
// @Success 200 {array} responses.GetListPostResponseItem
// @Failure 500 {object} responses.ErrorResponse
// @Router /post [get]
func (r *PostController) GetAll(c *gin.Context) {
result, err := r.service.GetAll()
if err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Internal server error")
c.IndentedJSON(resp.ErrorCode, resp)
return
}
res := dto.ResponseFromPostGetAllResult(result)
c.JSON(http.StatusOK, res)
}
// GetAllWithOffsetPost godoc
// @Summary Get posts after offset
// @Description return 5 posts after first offset posts
// @Tags post
// @Param offset query int true "Offset of posts"
// @Produce json
// @Success 200 {array} responses.GetListPostResponseItem
// @Router /post/{offset} [get]
func (r *PostController) GetAllWithOffset(c *gin.Context) {
}
// GetByIdPost godoc
// @Summary Get post by id
// @Description get post by id
// @Tags post
// @Param id query string true "Id of post"
// @Produce json
// @Success 200 {array} responses.PostResponse
// @Failure 400 {object} responses.ErrorResponse
// @Router /post/{id} [get]
func (r *PostController) GetById(c *gin.Context) {
id := c.Param("id")
id_valid, err := uuid.Parse(id)
if err != nil {
log.Println("User get by id error: ", err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, "Invalid user id")
c.JSON(resp.ErrorCode, resp)
return
}
query := queries.PostFindByIdQuery{
Id: id_valid,
}
posts, err := r.service.FindById(query)
if err != nil {
log.Println("Post service error: ", err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, "Bad request")
c.JSON(resp.ErrorCode, resp)
return
}
result := dto.ResponseFormPostFindByIdResult(posts)
c.JSON(http.StatusOK, result)
}
// PutPost godoc
// @Summary Update post content
// @Description update post content
// @Tags post
// @Param id query string true "Id of post"
//
// @Param request body requests.PutPostRequest true "Post data"
//
// @Produce json
// @Success 200 {object} responses.PostResponse
// @Failure 400 {object} responses.ErrorResponse
// @Router /post/{id} [put]
func (r *PostController) Put(c *gin.Context) {
var request requests.PutPostRequest
if err := c.BindJSON(request); err != nil {
log.Println("Post request error: ", err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, "Bad request")
c.JSON(resp.ErrorCode, resp)
}
}
// Delete godoc
// @Summary Delete post
// @Description Delete post by id
// @Tags post
// @Param id query string true "Id of post"
// @Produce json
// @Success 200
// @Router /post/{id} [delete]
func (r *PostController) Delete(c *gin.Context) {
}

View file

@ -0,0 +1,50 @@
package controllers
import (
"58team_blog/internal/application/services"
"github.com/gin-gonic/gin"
)
type UserController struct {
service *services.UserService
}
func CreateUserController(service *services.UserService) UserController {
return UserController{
service: service,
}
}
// @Summary Create new user
// @Description Creates new user in system
// @Param path query string true "Path to image"
// @Produce
// @Success 200
// @Router /images/{path} [get]
func (r *UserController) Post(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
func (r *UserController) FindById(c *gin.Context) {
}
func (r *UserController) FindByName(c *gin.Context) {
}
func (r *UserController) GetAll(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
func (r *UserController) Put(c *gin.Context) {
}
func (r *UserController) Delete(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}

View file

@ -0,0 +1,19 @@
package dto
import (
"58team_blog/internal/application/queries"
"58team_blog/internal/interfaces/api/responses"
)
func ResponseFormPostFindByIdResult(result *queries.PostFindByIdResult) responses.PostResponse {
res := result.Result
return responses.PostResponse{
Id: res.Id.String(),
UserId: res.UserId.String(),
Title: res.Title,
Description: res.Description,
Content: res.Content,
CreatedAt: res.CreatedAt,
UpdatedAt: res.UpdatedAt,
}
}

View file

@ -0,0 +1,25 @@
package dto
import (
"58team_blog/internal/application/common"
"58team_blog/internal/application/queries"
"58team_blog/internal/interfaces/api/responses"
)
func itemFromResult(item *common.PostResult) responses.GetListPostResponseItem {
return responses.GetListPostResponseItem{
Id: item.Id.String(),
Title: item.Title,
Description: item.Description,
}
}
func ResponseFromPostGetAllResult(result *queries.PostGetAllResult) responses.GetListPostResponse {
var resp []responses.GetListPostResponseItem
for _, r := range result.Result.Result {
resp = append(resp, itemFromResult(r))
}
return resp
}

View file

@ -0,0 +1,18 @@
package dto
import (
"58team_blog/internal/application/common"
"58team_blog/internal/interfaces/api/responses"
)
func ResponseFromPostResult(result *common.PostResult) responses.PostResponse {
return responses.PostResponse{
Id: result.Id.String(),
UserId: result.UserId.String(),
Title: result.Title,
Description: result.Description,
Content: result.Content,
CreatedAt: result.CreatedAt,
UpdatedAt: result.UpdatedAt,
}
}

View file

@ -1 +0,0 @@
package api

View file

@ -1 +0,0 @@
package api

View file

@ -0,0 +1,8 @@
package requests
type CreatePostRequest struct {
Title string `json:"title" validate:"required,min=8,max=255"`
Description string `json:"description" validate:"required,min=8,max=255"`
Content string `json:"content" validate:"required,min=36"`
UserId string `json:"userId" validate:"required,uuid5"`
}

View file

@ -0,0 +1,7 @@
package requests
type PutPostRequest struct {
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
}

View file

@ -0,0 +1,13 @@
package responses
type ErrorResponse struct {
ErrorCode int `json:"error_code"`
Message string `json:"message"`
}
func CreateErrorResponse(code int, msg string) ErrorResponse {
return ErrorResponse{
ErrorCode: code,
Message: msg,
}
}

View file

@ -0,0 +1,9 @@
package responses
type GetListPostResponseItem struct {
Id string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
}
type GetListPostResponse []GetListPostResponseItem

View file

@ -0,0 +1,15 @@
package responses
import "time"
type PostResponse struct {
Id string `json:"id"`
UserId string `json:"userId"`
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type PostResponseList []*PostResponse

View file

@ -1 +0,0 @@
package api

View file

@ -1 +1,18 @@
package interfaces
import (
"58team_blog/internal/application/services"
"58team_blog/internal/interfaces/api/controllers"
"github.com/gin-gonic/gin"
)
func BindPostAdmin(service *services.PostService, group *gin.RouterGroup) {
post := controllers.CreatePostController(service)
g := group.Group("/post")
g.GET("/", post.GetAllPost)
g.GET("/:id", post.GetByIdPost)
g.POST("/", post.PostPost)
g.PUT("/:id", post.PutPost)
}

BIN
migrate Executable file

Binary file not shown.

98
migrate.go Normal file
View file

@ -0,0 +1,98 @@
package main
import (
"58team_blog/internal/infrastructure"
"database/sql"
"flag"
"fmt"
"log"
"github.com/golang-migrate/migrate"
"github.com/golang-migrate/migrate/database/postgres"
_ "github.com/golang-migrate/migrate/source/file"
_ "github.com/lib/pq"
)
func main() {
var command = flag.String("command", "up", "Migration command: up, down, version, force.")
var steps = flag.Int("steps", -1, "Number of migration steps (for up/down commands)")
var version = flag.Int("version", -1, "Target version (for force command)")
config, err := infrastructure.LoadConfig()
if err != nil {
log.Fatal("Cannot load config file: ", err)
}
DbUrl := "postgres://" + config.DBUser + ":" + config.DBPass +
"@" + config.DBHost + ":" + config.DBPort + "/" + config.DBName + "?sslmode=disable"
fmt.Println(DbUrl)
db, err := sql.Open("postgres", DbUrl)
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
defer db.Close()
driver, err := postgres.WithInstance(db, &postgres.Config{})
if err != nil {
log.Fatal("Failed to create database driver:", err)
}
m, err := migrate.NewWithDatabaseInstance("file://./migrations", "postgres", driver)
if err != nil {
log.Fatal("Failed to create migrate instance:", err)
}
defer m.Close()
switch *command {
case "up":
if *steps > 0 {
err = m.Steps(*steps)
} else {
err = m.Up()
}
if err != nil && err != migrate.ErrNoChange {
log.Fatal("Migration up failed:", err)
}
if err == migrate.ErrNoChange {
fmt.Println("No migrations to apply")
} else {
fmt.Println("Migrations applied successfully")
}
case "down":
if *steps > 0 {
err = m.Steps(-*steps)
} else {
err = m.Down()
}
if err != nil && err != migrate.ErrNoChange {
log.Fatal("Migration down failed:", err)
}
if err == migrate.ErrNoChange {
fmt.Println("No migrations to rollback")
} else {
fmt.Println("Migrations rolled back successfully")
}
case "version":
version, dirty, err := m.Version()
if err != nil {
log.Fatal("Failed to get version:", err)
}
fmt.Printf("Current version: %d (dirty: %v)\n", version, dirty)
case "force":
if *version < 0 {
log.Fatal("Version is required for force command. Use -version flag")
}
err = m.Force(*version)
if err != nil {
log.Fatal("Force migration failed:", err)
}
fmt.Printf("Forced migration to version %d\n", *version)
default:
log.Fatal("Unknown command:", *command)
}
}

View file

@ -0,0 +1,3 @@
DROP TABLE IF EXISTS images;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS post;

View file

@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS post (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
content TEXT NOT NULL,
createdAt TIMESTAMP NOT NULL,
updatedAt TIMESTAMP NOT NULL,
FOREIGN KEY (userId) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS images (
id TEXT PRIMARY KEY,
path TEXT NOT NULL
);

View file

@ -1,48 +0,0 @@
CREATE TABLE "users" (
"id" TEXT NOT NULL UNIQUE,
"username" VARCHAR(255) UNIQUE,
"password" VARCHAR(255),
PRIMARY KEY("id")
);
CREATE TABLE "post" (
"id" TEXT NOT NULL UNIQUE,
"userId" TEXT NOT NULL UNIQUE,
"title" TEXT,
"description" TEXT,
"content" TEXT,
"createdAt" TIMESTAMP,
"updatedAt" TIMESTAMP,
PRIMARY KEY("id")
);
CREATE TABLE "posts" (
"id" TEXT NOT NULL UNIQUE,
"user_id" INTEGER,
"post_id" INTEGER,
PRIMARY KEY("id")
);
CREATE TABLE "images" (
"id" TEXT NOT NULL UNIQUE,
"path" TEXT,
PRIMARY KEY("id")
);
ALTER TABLE "users"
ADD FOREIGN KEY("id") REFERENCES "posts"("user_id")
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE "post"
ADD FOREIGN KEY("id") REFERENCES "posts"("post_id")
ON UPDATE NO ACTION ON DELETE NO ACTION;