├── .gitignore ├── Dockerfile ├── README.md ├── conf └── config.ini ├── controller └── controller.go ├── dao └── mysql.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── init.sql ├── main.go ├── models └── todo.go ├── routers └── routers.go ├── setting └── setting.go ├── static ├── css │ ├── app.8eeeaf31.css │ └── chunk-vendors.57db8905.css ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf └── js │ ├── app.007f9690.js │ └── chunk-vendors.ddcb6f91.js ├── templates ├── favicon.ico └── index.html └── wait-for.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .idea 15 | *.DS_Store 16 | bubble -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | # 为我们的镜像设置必要的环境变量 4 | ENV GO111MODULE=on \ 5 | GOPROXY=https://goproxy.cn,direct \ 6 | CGO_ENABLED=0 \ 7 | GOOS=linux \ 8 | GOARCH=amd64 9 | 10 | # 移动到工作目录:/build 11 | WORKDIR /build 12 | 13 | # 将代码复制到容器中 14 | COPY . . 15 | 16 | # 下载依赖信息 17 | RUN go mod download 18 | 19 | # 将代码复制到容器中 20 | COPY . . 21 | 22 | # 将我们的代码编译成二进制可执行文件 bubble 23 | RUN go build -o bubble . 24 | 25 | # 接下来创建一个小镜像 26 | FROM debian:stretch-slim 27 | 28 | # 从builder镜像中把脚本拷贝到当前目录 29 | COPY ./wait-for.sh / 30 | 31 | # 从builder镜像中把静态文件拷贝到当前目录 32 | COPY ./templates /templates 33 | COPY ./static /static 34 | 35 | # 从builder镜像中把配置文件拷贝到当前目录 36 | COPY ./conf /conf 37 | 38 | 39 | # 从builder镜像中把/dist/app 拷贝到当前目录 40 | COPY --from=builder /build/bubble / 41 | 42 | RUN set -eux; \ 43 | apt-get update; \ 44 | apt-get install -y \ 45 | --no-install-recommends \ 46 | netcat; \ 47 | chmod 755 wait-for.sh 48 | 49 | # 需要运行的命令 50 | # 如果使用docker compose模式需要将下面这一行注释掉!!! 51 | ENTRYPOINT ["/bubble", "conf/config.ini"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deploy_bubble_using_docker 2 | 3 | 4 | 5 | ## 说明 6 | 7 | [bubble](https://github.com/Q1mi/bubble)是我用gin框架和GORM框架编写的一个简单的Web程序。 8 | 9 | 这个仓库是使用Docker部署bubble的示例。 10 | 11 | 12 | ## 两种部署方式 13 | 因为该程序用到了MySQL,所以在使用Docker部署的时候需要用到两个容器环境。 14 | 15 | 我们需要让两个容器联通合作让我们的web程序正常运行起来。 16 | 17 | ### --link模式 18 | 19 | 1. 先启动一个MySQL容器 20 | 21 | ```bash 22 | docker run --name mysql8019 -p 13306:3306 -e MYSQL_ROOT_PASSWORD=root1234 -v /Users/q1mi/docker/mysql:/var/lib/mysql -d mysql:8.0.19 23 | ``` 24 | 2. 构建我们的bubble_app 镜像 25 | 26 | 具体行为参照Dockerfile中描述 27 | 28 | ```bash 29 | docker build . -t bubble_app 30 | ``` 31 | 3. --link模式 启动bubble_app容器 32 | ```bash 33 | docker run --link=mysql8019:mysql8019 -p 8888:8888 bubble_app 34 | ``` 35 | 36 | ### Docker Compose模式 37 | 38 | 具体行为参照`docker-compose.yml`文件中描述 39 | 40 | 直接执行下列命令启动容器: 41 | ```bash 42 | docker-compose up 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /conf/config.ini: -------------------------------------------------------------------------------- 1 | port = 8888 2 | release = false 3 | 4 | [mysql] 5 | user = root 6 | password = root1234 7 | host = mysql8019 8 | port = 3306 9 | db = bubble 10 | 11 | -------------------------------------------------------------------------------- /controller/controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "bubble/models" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | /* 10 | url --> controller --> logic --> model 11 | 请求来了 --> 控制器 --> 业务逻辑 --> 模型层的增删改查 12 | */ 13 | 14 | func IndexHandler(c *gin.Context) { 15 | c.HTML(http.StatusOK, "index.html", nil) 16 | } 17 | 18 | 19 | func CreateTodo(c *gin.Context) { 20 | // 前端页面填写待办事项 点击提交 会发请求到这里 21 | // 1. 从请求中把数据拿出来 22 | var todo models.Todo 23 | c.BindJSON(&todo) 24 | // 2. 存入数据库 25 | err:=models.CreateATodo(&todo) 26 | if err != nil{ 27 | c.JSON(http.StatusOK, gin.H{"error": err.Error()}) 28 | }else{ 29 | c.JSON(http.StatusOK, todo) 30 | //c.JSON(http.StatusOK, gin.H{ 31 | // "code": 2000, 32 | // "msg": "success", 33 | // "data": todo, 34 | //}) 35 | } 36 | } 37 | 38 | func GetTodoList(c *gin.Context) { 39 | // 查询todo这个表里的所有数据 40 | todoList, err := models.GetAllTodo() 41 | if err!= nil { 42 | c.JSON(http.StatusOK, gin.H{"error": err.Error()}) 43 | }else { 44 | c.JSON(http.StatusOK, todoList) 45 | } 46 | } 47 | 48 | func UpdateATodo(c *gin.Context) { 49 | id, ok := c.Params.Get("id") 50 | if !ok { 51 | c.JSON(http.StatusOK, gin.H{"error": "无效的id"}) 52 | return 53 | } 54 | todo, err := models.GetATodo(id) 55 | if err != nil { 56 | c.JSON(http.StatusOK, gin.H{"error": err.Error()}) 57 | return 58 | } 59 | c.BindJSON(&todo) 60 | if err = models.UpdateATodo(todo); err!= nil{ 61 | c.JSON(http.StatusOK, gin.H{"error": err.Error()}) 62 | }else{ 63 | c.JSON(http.StatusOK, todo) 64 | } 65 | } 66 | 67 | func DeleteATodo(c *gin.Context) { 68 | id, ok := c.Params.Get("id") 69 | if !ok { 70 | c.JSON(http.StatusOK, gin.H{"error": "无效的id"}) 71 | return 72 | } 73 | if err := models.DeleteATodo(id);err!=nil{ 74 | c.JSON(http.StatusOK, gin.H{"error": err.Error()}) 75 | }else{ 76 | c.JSON(http.StatusOK, gin.H{id:"deleted"}) 77 | } 78 | } -------------------------------------------------------------------------------- /dao/mysql.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "bubble/setting" 5 | "fmt" 6 | "github.com/jinzhu/gorm" 7 | _ "github.com/jinzhu/gorm/dialects/mysql" 8 | ) 9 | 10 | var ( 11 | DB *gorm.DB 12 | ) 13 | 14 | func InitMySQL(cfg *setting.MySQLConfig) (err error) { 15 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", 16 | cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB) 17 | DB, err = gorm.Open("mysql", dsn) 18 | if err != nil { 19 | return 20 | } 21 | return DB.DB().Ping() 22 | } 23 | 24 | func Close() { 25 | DB.Close() 26 | } 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # yaml 配置 2 | version: "3.7" 3 | services: 4 | mysql8019: 5 | image: "mysql:8.0.19" 6 | ports: 7 | - "33061:3306" 8 | command: "--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql" 9 | environment: 10 | MYSQL_ROOT_PASSWORD: "root1234" 11 | MYSQL_DATABASE: "bubble" 12 | MYSQL_PASSWORD: "root1234" 13 | volumes: 14 | - ./init.sql:/data/application/init.sql 15 | bubble_app: 16 | build: . 17 | command: sh -c "./wait-for.sh mysql8019:3306 -- ./bubble ./conf/config.ini" 18 | depends_on: 19 | - mysql8019 20 | ports: 21 | - "8888:8888" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module bubble 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.7 7 | github.com/jinzhu/gorm v1.9.12 8 | github.com/smartystreets/goconvey v1.7.2 // indirect 9 | gopkg.in/ini.v1 v1.55.0 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 5 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 6 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 7 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 8 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 9 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 10 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 11 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 12 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 13 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 14 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 15 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 16 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 17 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 18 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 19 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 20 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 21 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 22 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 23 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 24 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 25 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 26 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 27 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 28 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 29 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 30 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= 31 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 32 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 33 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 34 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 35 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 36 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 37 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 38 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 39 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 40 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 41 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 42 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 43 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 44 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 45 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 46 | github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= 47 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 48 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 49 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 50 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 51 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 52 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 53 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 54 | github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 55 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 56 | github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 57 | github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= 58 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 59 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 60 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 61 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 62 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 63 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 64 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 65 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 66 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 67 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 68 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 69 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 70 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 71 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 72 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 73 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 74 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 75 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 76 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 77 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 78 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 79 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 80 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 81 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 82 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 83 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 84 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 85 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 86 | gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ= 87 | gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 88 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 89 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 90 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 91 | -------------------------------------------------------------------------------- /init.sql: -------------------------------------------------------------------------------- 1 | 2 | -- create the databases 3 | CREATE DATABASE IF NOT EXISTS bubble; 4 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bubble/dao" 5 | "bubble/models" 6 | "bubble/routers" 7 | "bubble/setting" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) < 2 { 14 | fmt.Println("Usage:./bubble conf/config.ini") 15 | return 16 | } 17 | // 加载配置文件 18 | if err := setting.Init(os.Args[1]); err != nil { 19 | fmt.Println("load config from file failed, err:%v\n", err) 20 | return 21 | } 22 | // 创建数据库 23 | // sql: CREATE DATABASE bubble; 24 | // 连接数据库 25 | err := dao.InitMySQL(setting.Conf.MySQLConfig) 26 | if err != nil { 27 | fmt.Printf("init mysql failed, err:%v\n", err) 28 | return 29 | } 30 | defer dao.Close() // 程序退出关闭数据库连接 31 | // 模型绑定 32 | dao.DB.AutoMigrate(&models.Todo{}) 33 | // 注册路由 34 | r := routers.SetupRouter() 35 | if err := r.Run(fmt.Sprintf(":%d", setting.Conf.Port)); err != nil { 36 | fmt.Println("server startup failed, err:%v\n", err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /models/todo.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "bubble/dao" 5 | ) 6 | 7 | // Todo Model 8 | type Todo struct { 9 | ID int `json:"id"` 10 | Title string `json:"title"` 11 | Status bool `json:"status"` 12 | } 13 | 14 | /* 15 | Todo这个Model的增删改查操作都放在这里 16 | */ 17 | // CreateATodo 创建todo 18 | func CreateATodo(todo *Todo) (err error){ 19 | err = dao.DB.Create(&todo).Error 20 | return 21 | } 22 | 23 | func GetAllTodo() (todoList []*Todo, err error){ 24 | if err = dao.DB.Find(&todoList).Error; err != nil{ 25 | return nil, err 26 | } 27 | return 28 | } 29 | 30 | func GetATodo(id string)(todo *Todo, err error){ 31 | todo = new(Todo) 32 | if err = dao.DB.Debug().Where("id=?", id).First(todo).Error; err!=nil{ 33 | return nil, err 34 | } 35 | return 36 | } 37 | 38 | func UpdateATodo(todo *Todo)(err error){ 39 | err = dao.DB.Save(todo).Error 40 | return 41 | } 42 | 43 | func DeleteATodo(id string)(err error){ 44 | err = dao.DB.Where("id=?", id).Delete(&Todo{}).Error 45 | return 46 | } 47 | 48 | -------------------------------------------------------------------------------- /routers/routers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "bubble/controller" 5 | "bubble/setting" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func SetupRouter() *gin.Engine { 10 | if setting.Conf.Release { 11 | gin.SetMode(gin.ReleaseMode) 12 | } 13 | r := gin.Default() 14 | // 告诉gin框架模板文件引用的静态文件去哪里找 15 | r.Static("/static", "static") 16 | // 告诉gin框架去哪里找模板文件 17 | r.LoadHTMLGlob("templates/*") 18 | r.GET("/", controller.IndexHandler) 19 | 20 | // v1 21 | v1Group := r.Group("v1") 22 | { 23 | // 待办事项 24 | // 添加 25 | v1Group.POST("/todo", controller.CreateTodo) 26 | // 查看所有的待办事项 27 | v1Group.GET("/todo", controller.GetTodoList) 28 | // 修改某一个待办事项 29 | v1Group.PUT("/todo/:id", controller.UpdateATodo) 30 | // 删除某一个待办事项 31 | v1Group.DELETE("/todo/:id", controller.DeleteATodo) 32 | } 33 | return r 34 | } 35 | -------------------------------------------------------------------------------- /setting/setting.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "gopkg.in/ini.v1" 5 | ) 6 | 7 | var Conf = new(AppConfig) 8 | 9 | // AppConfig 应用程序配置 10 | type AppConfig struct { 11 | Release bool `ini:"release"` 12 | Port int `ini:"port"` 13 | *MySQLConfig `ini:"mysql"` 14 | } 15 | 16 | // MySQLConfig 数据库配置 17 | type MySQLConfig struct { 18 | User string `ini:"user"` 19 | Password string `ini:"password"` 20 | DB string `ini:"db"` 21 | Host string `ini:"host"` 22 | Port int `ini:"port"` 23 | } 24 | 25 | func Init(file string) error { 26 | return ini.MapTo(Conf, file) 27 | } 28 | -------------------------------------------------------------------------------- /static/css/app.8eeeaf31.css: -------------------------------------------------------------------------------- 1 | .el-table .warning-row{background:#fdf5e6}.el-table .success-row{text-decoration:line-through}.el-footer,.el-header{background-color:#409eff;color:#fff;text-align:center;line-height:60px}.el-footer{background-color:#909399;display:block;width:100%;position:fixed;bottom:0}body{margin:0} -------------------------------------------------------------------------------- /static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Q1mi/deploy_bubble_using_docker/bcbe21fdeaa83e7de83fcd97cac26324cf86ede2/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Q1mi/deploy_bubble_using_docker/bcbe21fdeaa83e7de83fcd97cac26324cf86ede2/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /static/js/app.007f9690.js: -------------------------------------------------------------------------------- 1 | (function(e){function t(t){for(var o,s,i=t[0],l=t[1],c=t[2],d=0,f=[];d ").concat(o),type:"success"})}))},handleDelete:function(e,t){var n=this;this.axios.delete("/v1/todo/"+t).then((function(){n.tableData.splice(e,1),n.$message({showClose:!0,duration:1500,message:"删除待办事项成功",type:"success"})}))},handleAdd:function(){var e=this;""!=this.newTitle?(this.axios.post("/v1/todo",{title:this.newTitle}).then((function(){e.getTodoList(),e.$message({showClose:!0,duration:1500,message:"添加待办事项成功",type:"success"})})),this.newTitle=""):this.$message({showClose:!0,duration:1500,message:"title不能为空哦",type:"warning"})}}}),v=h,w=(n("ed30"),n("2877")),b=Object(w["a"])(v,f,p,!1,null,null,null),m=b.exports,g={name:"Index",components:{TodoList:m}},x=g,y=(n("8fc1"),Object(w["a"])(x,u,d,!1,null,null,null)),j=y.exports,_={name:"app",components:{Index:j}},O=_,T=(n("034f"),Object(w["a"])(O,l,c,!1,null,null,null)),P=T.exports,$=n("5c96"),k=n.n($);n("0fae");o["default"].use(k.a);var C=n("8c4f");o["default"].use(C["a"]);var D=[{path:"/",name:"index",component:j}],S=new C["a"]({routes:D}),E=S;o["default"].config.productionTip=!0,new o["default"]({router:E,render:function(e){return e(P)}}).$mount("#app")},"85ec":function(e,t,n){},"89d2":function(e,t,n){},"8fc1":function(e,t,n){"use strict";var o=n("9272"),r=n.n(o);r.a},9272:function(e,t,n){},ed30:function(e,t,n){"use strict";var o=n("89d2"),r=n.n(o);r.a}}); -------------------------------------------------------------------------------- /templates/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Q1mi/deploy_bubble_using_docker/bcbe21fdeaa83e7de83fcd97cac26324cf86ede2/templates/favicon.ico -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | bubble清单
-------------------------------------------------------------------------------- /wait-for.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TIMEOUT=15 4 | QUIET=0 5 | 6 | ADDRS=() 7 | 8 | echoerr() { 9 | if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi 10 | } 11 | 12 | usage() { 13 | exitcode="$1" 14 | cat << USAGE >&2 15 | client: 16 | $cmdname host:port [host:port] [host:port] [-t timeout] [-- command args] 17 | -q | --quiet Do not output any status messages 18 | -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout 19 | -- COMMAND ARGS Execute command with args after the test finishes 20 | USAGE 21 | exit "$exitcode" 22 | } 23 | 24 | wait_for() { 25 | results=() 26 | for addr in ${ADDRS[@]} 27 | do 28 | HOST=$(printf "%s\n" "$addr"| cut -d : -f 1) 29 | PORT=$(printf "%s\n" "$addr"| cut -d : -f 2) 30 | result=1 31 | for i in `seq $TIMEOUT` ; do 32 | nc -z "$HOST" "$PORT" > /dev/null 2>&1 33 | result=$? 34 | if [ $result -ne 0 ] ; then 35 | sleep 1 36 | continue 37 | fi 38 | break 39 | done 40 | results=(${results[@]} $result) 41 | done 42 | num=${#results[@]} 43 | for result in ${results[@]} 44 | do 45 | if [ $result -eq 0 ] ; then 46 | num=`expr $num - 1` 47 | fi 48 | done 49 | if [ $num -eq 0 ] ; then 50 | if [ $# -gt 0 ] ; then 51 | exec "$@" 52 | fi 53 | exit 0 54 | fi 55 | echo "Operation timed out" >&2 56 | exit 1 57 | } 58 | 59 | while [ $# -gt 0 ] 60 | do 61 | case "$1" in 62 | *:* ) 63 | ADDRS=(${ADDRS[@]} $1) 64 | shift 1 65 | ;; 66 | -q | --quiet) 67 | QUIET=1 68 | shift 1 69 | ;; 70 | -t) 71 | TIMEOUT="$2" 72 | if [ "$TIMEOUT" = "" ]; then break; fi 73 | shift 2 74 | ;; 75 | --timeout=*) 76 | TIMEOUT="${1#*=}" 77 | shift 1 78 | ;; 79 | --) 80 | shift 81 | break 82 | ;; 83 | --help) 84 | usage 0 85 | ;; 86 | *) 87 | echoerr "Unknown argument: $1" 88 | usage 1 89 | ;; 90 | esac 91 | done 92 | 93 | if [ "${#ADDRS[@]}" -eq 0 ]; then 94 | echoerr "Error: you need to provide a host and port to test." 95 | usage 2 96 | fi 97 | 98 | wait_for "$@" --------------------------------------------------------------------------------