├── .github └── workflows │ ├── go.yml │ └── release.yaml ├── .gitignore ├── .goreleaser.yaml ├── .vscode └── launch.json ├── DBPacth └── v1.0.1-v1.0.2.sql ├── LICENSE ├── README.md ├── code-push.sql ├── config ├── app.json ├── app.prod.json └── config.go ├── db ├── db.go └── redis │ └── redis.go ├── go.mod ├── go.sum ├── main.go ├── middleware └── middleware.go ├── model ├── app.go ├── base.go ├── constants │ ├── constants.go │ └── token_info.go ├── deployment.go ├── deploymentVersion.go ├── package.go ├── token.go └── user.go ├── request ├── app.go ├── client.go └── user.go └── utils └── utils.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | pull_request_review: 12 | types: [submitted, dismissed] 13 | 14 | jobs: 15 | 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@v4 23 | with: 24 | go-version: '1.21.5' 25 | - name: Install dependencies 26 | run: go get . 27 | - name: Build 28 | run: go build -v ./... 29 | - name: Test with the Go CLI 30 | run: go test 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | # run only against tags 6 | tags: 7 | - '*' 8 | 9 | permissions: 10 | contents: write 11 | # packages: write 12 | # issues: write 13 | 14 | jobs: 15 | goreleaser: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | - run: git fetch --force --tags 22 | - uses: actions/setup-go@v3 23 | with: 24 | go-version: '1.21.5' 25 | cache: true 26 | # More assembly might be required: Docker logins, GPG, etc. It all depends 27 | # on your needs. 28 | - uses: goreleaser/goreleaser-action@v4 29 | with: 30 | # either 'goreleaser' (default) or 'goreleaser-pro': 31 | distribution: goreleaser 32 | version: latest 33 | args: release --clean 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' 37 | # distribution: 38 | # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config/app.dev.json 2 | bundels/* 3 | __debug_bin* -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sensible defaults. 2 | # Make sure to check the documentation at https://goreleaser.com 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod tidy 7 | # you may remove this if you don't need go generate 8 | - go generate ./... 9 | builds: 10 | - env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - linux 14 | - windows 15 | - darwin 16 | 17 | archives: 18 | - format: tar.gz 19 | # this name template makes the OS and Arch compatible with the results of uname. 20 | name_template: >- 21 | {{ .ProjectName }}_ 22 | {{- title .Os }}_ 23 | {{- if eq .Arch "amd64" }}x86_64 24 | {{- else if eq .Arch "386" }}i386 25 | {{- else }}{{ .Arch }}{{ end }} 26 | {{- if .Arm }}v{{ .Arm }}{{ end }} 27 | # use zip for windows archives 28 | format_overrides: 29 | - goos: windows 30 | format: zip 31 | checksum: 32 | name_template: 'checksums.txt' 33 | snapshot: 34 | name_template: "{{ incpatch .Version }}-next" 35 | changelog: 36 | sort: asc 37 | filters: 38 | exclude: 39 | - '^docs:' 40 | - '^test:' 41 | 42 | # The lines beneath this are called `modelines`. See `:help modeline` 43 | # Feel free to remove those if you don't want/use them. 44 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 45 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}/main.go" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /DBPacth/v1.0.1-v1.0.2.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `package` 2 | ADD COLUMN `deployment_version_id` INT NULL AFTER `deployment_id`; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 htdcx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # code-push-server-go 2 | Codepush server go is compatible with [react-native-code-push](https://github.com/microsoft/react-native-code-push). Need to be used with [code-push-go](https://github.com/htdcx/code-push-go). Only supported react-native 3 | 4 | ## Support version 5 | - [mysql](https://dev.mysql.com/downloads/mysql/) >= 8.0 6 | - [golang](https://go.dev/dl/) >= 1.21.5 7 | - [redis](https://redis.io/downloads/) >= 5.0 8 | 9 | ## Support client version 10 | - [react-native-code-push](https://github.com/microsoft/react-native-code-push) >= 7.0 11 | 12 | ## Support storage 13 | - Local 14 | - AWS S3 15 | - FTP 16 | 17 | ## Before installation, please ensure that the following procedures have been installed 18 | - mysql 19 | - golang 20 | - redis 21 | 22 | ## Install code-push-server 23 | ```shell 24 | git clone https://github.com/htdcx/code-push-server-go.git 25 | cd code-push-server-go 26 | import code-push.sql to mysql 27 | ``` 28 | ### Configuration mysql,redis,storage 29 | ``` shell 30 | cd config 31 | vi (app.json or app.dev.json or app.prod.json) 32 | # app.json 33 | { 34 | "mode":"prod" #run read config app.{mode}.json 35 | } 36 | # app.prod.json 37 | { 38 | "DBUser": { 39 | "Write": { 40 | "UserName": "", 41 | "Password": "", 42 | "Host": "127.0.0.1", 43 | "Port": 3306, 44 | "DBname": "" 45 | }, 46 | "MaxIdleConns": 10, 47 | "MaxOpenConns": 100, 48 | "ConnMaxLifetime": 1 49 | }, 50 | "Redis": { 51 | "Host": "127.0.0.1", 52 | "Port": 6379, 53 | "DBIndex": 0, 54 | "UserName": "", 55 | "Password": "" 56 | }, 57 | "CodePush": { 58 | "FileLocal":(local,aws,ftp), 59 | "Local":{ 60 | "SavePath":"./bundles" 61 | }, 62 | "Aws":{ 63 | "Endpoint":"", 64 | "Region":"", 65 | "S3ForcePathStyle":true, 66 | "KeyId":"", 67 | "Secret":"", 68 | "Bucket":"" 69 | }, 70 | "Ftp":{ 71 | "ServerUrl":"", 72 | "UserName":"", 73 | "Password":"" 74 | } 75 | }, 76 | "UrlPrefix": "/", 77 | "ResourceUrl": (nginx config url or s3), 78 | "Port": ":8080", 79 | "TokenExpireTime": 30 (day) 80 | } 81 | 82 | ``` 83 | #### Build 84 | ``` shell 85 | #MacOS pack GOOS:windows,darwin 86 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o code-push-server-go(.exe) main.go 87 | 88 | #Windows pack 89 | set GOARCH=amd64 90 | set GOOS=linux #windows,darwin 91 | go build -o code-push-server-go(.exe) main.go 92 | 93 | #copy config/app.(model).json and config/app.json to run dir 94 | 95 | #Linux server 96 | chmod +x code-push-server-go 97 | 98 | #run 99 | ./code-push-server-go 100 | ``` 101 | ### Default user name and password 102 | - Username:admin 103 | - Password:admin 104 | 105 | ### Change password and user name 106 | ``` shell 107 | Version >= 1.0.5 :./code-push-go change_password 108 | Version <= 1.0.4 :Change mysql users tables (password need md5) 109 | ``` 110 | 111 | ### Use [code-push-go](https://github.com/htdcx/code-push-go) 112 | ``` shell 113 | ./code-push-go login -u (userName) -p (password) -h (serverUrl) 114 | ``` 115 | ### Configuration client [react-native-code-push](https://github.com/microsoft/react-native-code-push) 116 | 117 | ``` shell 118 | #ios add to Info.plist 119 | CodePushServerURL 120 | ${CODE_PUSH_SERVER_URL} 121 | 122 | #android add to res/value/strings.xml 123 | ${CODE_PUSH_SERVER_URL} 124 | ``` 125 | 126 | ## License 127 | MIT License [Read](https://github.com/htdcx/code-push-server-go/blob/main/LICENSE) 128 | -------------------------------------------------------------------------------- /code-push.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `code-push` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */; 2 | USE `code-push`; 3 | -- MySQL dump 10.13 Distrib 8.0.34, for macos13 (arm64) 4 | -- 5 | -- Host: localhost Database: code-push 6 | -- ------------------------------------------------------ 7 | -- Server version 8.0.27 8 | 9 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 10 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 11 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 12 | /*!50503 SET NAMES utf8 */; 13 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 14 | /*!40103 SET TIME_ZONE='+00:00' */; 15 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 16 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 17 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 18 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 19 | 20 | -- 21 | -- Table structure for table `apps` 22 | -- 23 | 24 | DROP TABLE IF EXISTS `apps`; 25 | /*!40101 SET @saved_cs_client = @@character_set_client */; 26 | /*!50503 SET character_set_client = utf8mb4 */; 27 | CREATE TABLE `apps` ( 28 | `id` int NOT NULL AUTO_INCREMENT, 29 | `uid` int DEFAULT NULL, 30 | `app_name` varchar(256) DEFAULT NULL, 31 | `os` int DEFAULT NULL, 32 | `create_time` bigint DEFAULT NULL, 33 | PRIMARY KEY (`id`) 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 35 | /*!40101 SET character_set_client = @saved_cs_client */; 36 | 37 | -- 38 | -- Dumping data for table `apps` 39 | -- 40 | 41 | LOCK TABLES `apps` WRITE; 42 | /*!40000 ALTER TABLE `apps` DISABLE KEYS */; 43 | /*!40000 ALTER TABLE `apps` ENABLE KEYS */; 44 | UNLOCK TABLES; 45 | 46 | -- 47 | -- Table structure for table `deployment` 48 | -- 49 | 50 | DROP TABLE IF EXISTS `deployment`; 51 | /*!40101 SET @saved_cs_client = @@character_set_client */; 52 | /*!50503 SET character_set_client = utf8mb4 */; 53 | CREATE TABLE `deployment` ( 54 | `id` int NOT NULL AUTO_INCREMENT, 55 | `app_id` int DEFAULT NULL, 56 | `name` varchar(256) DEFAULT NULL, 57 | `key` varchar(256) DEFAULT NULL, 58 | `version_id` int DEFAULT NULL, 59 | `update_time` bigint DEFAULT NULL, 60 | `create_time` bigint DEFAULT NULL, 61 | PRIMARY KEY (`id`) 62 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 63 | /*!40101 SET character_set_client = @saved_cs_client */; 64 | 65 | -- 66 | -- Dumping data for table `deployment` 67 | -- 68 | 69 | LOCK TABLES `deployment` WRITE; 70 | /*!40000 ALTER TABLE `deployment` DISABLE KEYS */; 71 | /*!40000 ALTER TABLE `deployment` ENABLE KEYS */; 72 | UNLOCK TABLES; 73 | 74 | -- 75 | -- Table structure for table `deployment_version` 76 | -- 77 | 78 | DROP TABLE IF EXISTS `deployment_version`; 79 | /*!40101 SET @saved_cs_client = @@character_set_client */; 80 | /*!50503 SET character_set_client = utf8mb4 */; 81 | CREATE TABLE `deployment_version` ( 82 | `id` int NOT NULL AUTO_INCREMENT, 83 | `deployment_id` int DEFAULT NULL, 84 | `app_version` varchar(45) DEFAULT NULL, 85 | `version_num` bigint DEFAULT NULL, 86 | `current_package` int DEFAULT NULL, 87 | `update_time` bigint DEFAULT NULL, 88 | `create_time` bigint DEFAULT NULL, 89 | PRIMARY KEY (`id`) 90 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 91 | /*!40101 SET character_set_client = @saved_cs_client */; 92 | 93 | -- 94 | -- Dumping data for table `deployment_version` 95 | -- 96 | 97 | LOCK TABLES `deployment_version` WRITE; 98 | /*!40000 ALTER TABLE `deployment_version` DISABLE KEYS */; 99 | /*!40000 ALTER TABLE `deployment_version` ENABLE KEYS */; 100 | UNLOCK TABLES; 101 | 102 | -- 103 | -- Table structure for table `package` 104 | -- 105 | 106 | DROP TABLE IF EXISTS `package`; 107 | /*!40101 SET @saved_cs_client = @@character_set_client */; 108 | /*!50503 SET character_set_client = utf8mb4 */; 109 | CREATE TABLE `package` ( 110 | `id` int NOT NULL AUTO_INCREMENT, 111 | `deployment_id` int DEFAULT NULL, 112 | `deployment_version_id` int DEFAULT NULL, 113 | `size` bigint DEFAULT NULL, 114 | `hash` varchar(256) DEFAULT NULL, 115 | `description` TEXT DEFAULT NULL, 116 | `download` varchar(256) DEFAULT NULL, 117 | `active` int DEFAULT '0', 118 | `failed` int DEFAULT '0', 119 | `installed` int DEFAULT '0', 120 | `create_time` bigint DEFAULT NULL, 121 | PRIMARY KEY (`id`) 122 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 123 | /*!40101 SET character_set_client = @saved_cs_client */; 124 | 125 | -- `description` is not part of v1.0.1, it can be added by 126 | -- ALTER TABLE `package` ADD `description` TEXT DEFAULT NULL; 127 | 128 | -- 129 | -- Dumping data for table `package` 130 | -- 131 | 132 | LOCK TABLES `package` WRITE; 133 | /*!40000 ALTER TABLE `package` DISABLE KEYS */; 134 | /*!40000 ALTER TABLE `package` ENABLE KEYS */; 135 | UNLOCK TABLES; 136 | 137 | -- 138 | -- Table structure for table `token` 139 | -- 140 | 141 | DROP TABLE IF EXISTS `token`; 142 | /*!40101 SET @saved_cs_client = @@character_set_client */; 143 | /*!50503 SET character_set_client = utf8mb4 */; 144 | CREATE TABLE `token` ( 145 | `id` int NOT NULL AUTO_INCREMENT, 146 | `uid` int DEFAULT NULL, 147 | `token` varchar(256) DEFAULT NULL, 148 | `expire_time` bigint DEFAULT NULL, 149 | `del` int DEFAULT NULL, 150 | PRIMARY KEY (`id`) 151 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 152 | /*!40101 SET character_set_client = @saved_cs_client */; 153 | 154 | -- 155 | -- Dumping data for table `token` 156 | -- 157 | 158 | LOCK TABLES `token` WRITE; 159 | /*!40000 ALTER TABLE `token` DISABLE KEYS */; 160 | /*!40000 ALTER TABLE `token` ENABLE KEYS */; 161 | UNLOCK TABLES; 162 | 163 | -- 164 | -- Table structure for table `users` 165 | -- 166 | 167 | DROP TABLE IF EXISTS `users`; 168 | /*!40101 SET @saved_cs_client = @@character_set_client */; 169 | /*!50503 SET character_set_client = utf8mb4 */; 170 | CREATE TABLE `users` ( 171 | `id` int NOT NULL, 172 | `user_name` varchar(45) DEFAULT NULL, 173 | `password` varchar(45) DEFAULT NULL, 174 | PRIMARY KEY (`id`) 175 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; 176 | /*!40101 SET character_set_client = @saved_cs_client */; 177 | 178 | -- 179 | -- Dumping data for table `users` 180 | -- 181 | 182 | LOCK TABLES `users` WRITE; 183 | /*!40000 ALTER TABLE `users` DISABLE KEYS */; 184 | INSERT INTO `users` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3'); 185 | /*!40000 ALTER TABLE `users` ENABLE KEYS */; 186 | UNLOCK TABLES; 187 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 188 | 189 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 190 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 191 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 192 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 193 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 194 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 195 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 196 | 197 | -- Dump completed on 2024-04-22 17:35:58 198 | -------------------------------------------------------------------------------- /config/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode":"prod" 3 | } -------------------------------------------------------------------------------- /config/app.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "DBUser": { 3 | "Write": { 4 | "UserName": "root", 5 | "Password": "", 6 | "Host": "127.0.0.1", 7 | "Port": 3306, 8 | "DBname": "code-push" 9 | }, 10 | "MaxIdleConns": 10, 11 | "MaxOpenConns": 100, 12 | "ConnMaxLifetime": 1 13 | }, 14 | "Redis": { 15 | "Host": "127.0.0.1", 16 | "Port": 6379, 17 | "DBIndex": 0, 18 | "UserName": "", 19 | "Password": "" 20 | }, 21 | "CodePush": { 22 | "FileLocal":"local", 23 | "Local":{ 24 | "SavePath":"./bundles" 25 | }, 26 | "Aws":{ 27 | "Endpoint":"", 28 | "Region":"", 29 | "S3ForcePathStyle":true, 30 | "KeyId":"", 31 | "Secret":"", 32 | "Bucket":"" 33 | }, 34 | "Ftp":{ 35 | "ServerUrl":"", 36 | "UserName":"", 37 | "Password":"" 38 | } 39 | }, 40 | "UrlPrefix": "/", 41 | "ResourceUrl": "", 42 | "Port": ":8080", 43 | "TokenExpireTime": 30 44 | } -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | var configFile *appConfig 12 | 13 | func getExcPath() string { 14 | file, _ := exec.LookPath(os.Args[0]) 15 | // 获取包含可执行文件名称的路径 16 | path, _ := filepath.Abs(file) 17 | // 获取可执行文件所在目录 18 | index := strings.LastIndex(path, string(os.PathSeparator)) 19 | ret := path[:index] 20 | return strings.Replace(ret, "\\", "/", -1) 21 | } 22 | 23 | func GetConfig() appConfig { 24 | if configFile != nil { 25 | return *configFile 26 | } 27 | path := getExcPath() 28 | var mode modeConfig = readJson[modeConfig](path + "/config/app.json") 29 | 30 | appConfig := readJson[appConfig](path + "/config/app." + mode.Mode + ".json") 31 | configFile = &appConfig 32 | return *configFile 33 | } 34 | 35 | func readJson[T any](path string) T { 36 | file, err := os.Open(path) 37 | if err != nil { 38 | panic(path + " config not found") 39 | } 40 | defer file.Close() 41 | decoder := json.NewDecoder(file) 42 | 43 | var jsonF T 44 | decoder.Decode(&jsonF) 45 | return jsonF 46 | } 47 | 48 | type modeConfig struct { 49 | Mode string 50 | } 51 | 52 | type appConfig struct { 53 | DBUser dbConfig 54 | Redis redisConfig 55 | CodePush codePush 56 | UrlPrefix string 57 | Port string 58 | ResourceUrl string 59 | TokenExpireTime int64 60 | } 61 | type dbConfig struct { 62 | Write dbConfigObj 63 | MaxIdleConns uint 64 | MaxOpenConns uint 65 | ConnMaxLifetime uint 66 | } 67 | type dbConfigObj struct { 68 | UserName string 69 | Password string 70 | Host string 71 | Port uint 72 | DBname string 73 | } 74 | type redisConfig struct { 75 | Host string 76 | Port uint 77 | DBIndex uint 78 | UserName string 79 | Password string 80 | } 81 | type codePush struct { 82 | FileLocal string 83 | Local localConfig 84 | Aws awsConfig 85 | Ftp ftpConfig 86 | } 87 | type awsConfig struct { 88 | Endpoint string 89 | Region string 90 | S3ForcePathStyle bool 91 | KeyId string 92 | Secret string 93 | Bucket string 94 | } 95 | type ftpConfig struct { 96 | ServerUrl string 97 | UserName string 98 | Password string 99 | } 100 | type localConfig struct { 101 | SavePath string 102 | } 103 | -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "com.lc.go.codepush/server/config" 8 | "gorm.io/driver/mysql" 9 | "gorm.io/gorm" 10 | "gorm.io/gorm/logger" 11 | ) 12 | 13 | var ormDB *gorm.DB 14 | 15 | func GetUserDB() (odb *gorm.DB, err error) { 16 | if ormDB != nil { 17 | odb = ormDB 18 | return 19 | } 20 | dbConfig := config.GetConfig().DBUser 21 | dsnSource := dbConfig.Write.UserName + ":" + dbConfig.Write.Password + "@tcp(" + dbConfig.Write.Host + ":" + strconv.Itoa(int(dbConfig.Write.Port)) + ")/" + dbConfig.Write.DBname + "?charset=utf8mb4&parseTime=True&loc=Local" 22 | 23 | db, err := gorm.Open(mysql.Open(dsnSource), &gorm.Config{ 24 | Logger: logger.Default.LogMode(logger.Error), 25 | }) 26 | if err != nil { 27 | return 28 | } 29 | sqlDb, err := db.DB() 30 | if err != nil { 31 | return 32 | } 33 | sqlDb.SetMaxIdleConns(int(dbConfig.MaxIdleConns)) 34 | sqlDb.SetMaxOpenConns(int(dbConfig.MaxOpenConns)) 35 | sqlDb.SetConnMaxIdleTime(time.Duration(dbConfig.MaxIdleConns)) 36 | 37 | ormDB = db 38 | odb = ormDB 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /db/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "strconv" 9 | "time" 10 | 11 | "com.lc.go.codepush/server/config" 12 | "github.com/redis/go-redis/v9" 13 | ) 14 | 15 | var redisDB *redis.Client 16 | var ctx context.Context = context.Background() 17 | 18 | func GetRedis() (redisD *redis.Client, err error) { 19 | if redisDB != nil { 20 | redisD = redisDB 21 | return 22 | } 23 | redisConfig := config.GetConfig().Redis 24 | redisDB = redis.NewClient(&redis.Options{ 25 | Addr: redisConfig.Host + ":" + strconv.Itoa(int(redisConfig.Port)), 26 | Username: redisConfig.UserName, 27 | Password: redisConfig.Password, 28 | DB: int(redisConfig.DBIndex), 29 | }) 30 | redisD = redisDB 31 | return 32 | } 33 | 34 | func SetRedisObj(key string, obj any, duration time.Duration) { 35 | redis, _ := GetRedis() 36 | jData, _ := json.Marshal(obj) 37 | status := redis.Set(ctx, key, string(jData), duration) 38 | if err := status.Err(); err != nil { 39 | fmt.Println(err.Error()) 40 | } 41 | } 42 | 43 | func DelRedisObj(key string) { 44 | redis, _ := GetRedis() 45 | 46 | iter := redis.Scan(ctx, 0, key, 0).Iterator() 47 | for iter.Next(ctx) { 48 | err := redis.Del(ctx, iter.Val()).Err() 49 | if err != nil { 50 | panic("Redis error:" + err.Error()) 51 | } 52 | } 53 | if err := iter.Err(); err != nil { 54 | panic("Redis error:" + err.Error()) 55 | } 56 | } 57 | 58 | func GetRedisObj[T any](key string) *T { 59 | 60 | redis, _ := GetRedis() 61 | status := redis.Get(ctx, key) 62 | if err := status.Err(); err != nil { 63 | log.Println(err.Error()) 64 | return nil 65 | } 66 | var obj T 67 | err := json.Unmarshal([]byte(status.Val()), &obj) 68 | if err != nil { 69 | panic("Redis error:" + err.Error()) 70 | } 71 | return &obj 72 | } 73 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module com.lc.go.codepush/server 2 | 3 | go 1.21.5 4 | 5 | require gorm.io/driver/mysql v1.5.6 6 | 7 | require ( 8 | github.com/bytedance/sonic v1.11.3 // indirect 9 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 10 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 11 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 12 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 13 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 14 | github.com/gin-contrib/sse v0.1.0 // indirect 15 | github.com/go-playground/locales v0.14.1 // indirect 16 | github.com/go-playground/universal-translator v0.18.1 // indirect 17 | github.com/go-playground/validator/v10 v10.19.0 // indirect 18 | github.com/goccy/go-json v0.10.2 // indirect 19 | github.com/hashicorp/errwrap v1.1.0 // indirect 20 | github.com/hashicorp/go-multierror v1.1.1 // indirect 21 | github.com/jlaffaye/ftp v0.2.0 // indirect 22 | github.com/jmespath/go-jmespath v0.4.0 // indirect 23 | github.com/json-iterator/go v1.1.12 // indirect 24 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 25 | github.com/leodido/go-urn v1.4.0 // indirect 26 | github.com/mattn/go-isatty v0.0.20 // indirect 27 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 28 | github.com/modern-go/reflect2 v1.0.2 // indirect 29 | github.com/pelletier/go-toml/v2 v2.2.0 // indirect 30 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 31 | github.com/ugorji/go/codec v1.2.12 // indirect 32 | golang.org/x/arch v0.7.0 // indirect 33 | golang.org/x/crypto v0.21.0 // indirect 34 | golang.org/x/net v0.22.0 // indirect 35 | golang.org/x/sys v0.18.0 // indirect 36 | golang.org/x/text v0.14.0 // indirect 37 | google.golang.org/protobuf v1.33.0 // indirect 38 | gopkg.in/yaml.v3 v3.0.1 // indirect 39 | ) 40 | 41 | require ( 42 | github.com/aws/aws-sdk-go v1.51.24 43 | github.com/gin-contrib/gzip v1.0.0 44 | github.com/gin-gonic/gin v1.9.1 45 | github.com/go-sql-driver/mysql v1.7.0 // indirect 46 | github.com/google/uuid v1.6.0 47 | github.com/jinzhu/inflection v1.0.0 // indirect 48 | github.com/jinzhu/now v1.1.5 // indirect 49 | github.com/redis/go-redis/v9 v9.5.1 50 | gorm.io/gorm v1.25.9 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aws/aws-sdk-go v1.51.24 h1:nwL5MaommPkwb7Ixk24eWkdx5HY4of1gD10kFFVAl6A= 2 | github.com/aws/aws-sdk-go v1.51.24/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= 3 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 4 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 5 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 6 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= 7 | github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= 8 | github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 9 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 10 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 11 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 12 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 13 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 14 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 15 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 16 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 17 | github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= 18 | github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 22 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 23 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 24 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 25 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 26 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 27 | github.com/gin-contrib/gzip v1.0.0 h1:UKN586Po/92IDX6ie5CWLgMI81obiIp5nSP85T3wlTk= 28 | github.com/gin-contrib/gzip v1.0.0/go.mod h1:CtG7tQrPB3vIBo6Gat9FVUsis+1emjvQqd66ME5TdnE= 29 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 30 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 31 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 32 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 33 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 34 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 35 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 36 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 37 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 38 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 39 | github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= 40 | github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 41 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 42 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 43 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 44 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 45 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 46 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 47 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 48 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 49 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 50 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 51 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 52 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 53 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 54 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 55 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 56 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 57 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 58 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 59 | github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= 60 | github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= 61 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 62 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 63 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 64 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 65 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 66 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 67 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 68 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 69 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 70 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 71 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 72 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 73 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 74 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 75 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 76 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 77 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 78 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 79 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 80 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 81 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 82 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 83 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 84 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 85 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 86 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 87 | github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= 88 | github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 89 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 90 | github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= 91 | github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= 92 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 93 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 94 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 95 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 96 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 97 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 98 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 99 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 100 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 101 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 102 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 103 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 104 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 105 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 106 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 107 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 108 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 109 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 110 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 111 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 112 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 113 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 114 | golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= 115 | golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 116 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 117 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 118 | golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= 119 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 120 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 121 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 122 | golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= 123 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 124 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 125 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 126 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 127 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 128 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 129 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 130 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 131 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 132 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 133 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 134 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 135 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 136 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 137 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 138 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 139 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 140 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 141 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 142 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 143 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 144 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 145 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 146 | gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= 147 | gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= 148 | gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= 149 | gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 150 | gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= 151 | gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 152 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 153 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 154 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "com.lc.go.codepush/server/config" 7 | "com.lc.go.codepush/server/middleware" 8 | "com.lc.go.codepush/server/request" 9 | 10 | "github.com/gin-contrib/gzip" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func main() { 16 | fmt.Println("code-push-server-go V1.0.5") 17 | // gin.SetMode(gin.ReleaseMode) 18 | g := gin.Default() 19 | g.Use(gzip.Gzip(gzip.DefaultCompression)) 20 | g.Use(middleware.Recover) 21 | configs := config.GetConfig() 22 | 23 | // g.Static("/bundels", "bundels") 24 | 25 | g.GET("/v0.1/public/codepush/update_check", request.Client{}.CheckUpdate) 26 | g.POST("/v0.1/public/codepush/report_status/deploy", request.Client{}.ReportStatus) 27 | g.POST("/v0.1/public/codepush/report_status/download", request.Client{}.Download) 28 | 29 | apiGroup := g.Group(configs.UrlPrefix) 30 | { 31 | apiGroup.POST("/login", request.User{}.Login) 32 | } 33 | authApi := apiGroup.Use(middleware.CheckToken) 34 | { 35 | authApi.POST("/createApp", request.App{}.CreateApp) 36 | authApi.POST("/createDeployment", request.App{}.CreateDeployment) 37 | authApi.POST("/createBundle", request.App{}.CreateBundle) 38 | authApi.POST("/checkBundle", request.App{}.CheckBundle) 39 | authApi.POST("/delApp", request.App{}.DelApp) 40 | authApi.POST("/delDeployment", request.App{}.DelDeployment) 41 | authApi.POST("/lsDeployment", request.App{}.LsDeployment) 42 | authApi.GET("/lsApp", request.App{}.LsApp) 43 | authApi.POST("/uploadBundle", request.App{}.UploadBundle) 44 | authApi.POST("/rollback", request.App{}.Rollback) 45 | authApi.POST("/changePassword", request.User{}.ChangePassword) 46 | } 47 | 48 | g.Run(configs.Port) 49 | } 50 | -------------------------------------------------------------------------------- /middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "reflect" 8 | 9 | "com.lc.go.codepush/server/model" 10 | "com.lc.go.codepush/server/model/constants" 11 | "com.lc.go.codepush/server/utils" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | // 檢查token 16 | func CheckToken(ctx *gin.Context) { 17 | var token, _ = ctx.Cookie("token") 18 | if token == "" { 19 | token = ctx.GetHeader("token") 20 | } 21 | 22 | if token == "" { 23 | log.Panic("Token can't null") 24 | } 25 | 26 | tokenNow := model.GetOne[model.Token]("token=?", token) 27 | 28 | if *utils.GetTimeNow() > *tokenNow.ExpireTime || *tokenNow.Del { 29 | ctx.JSON(http.StatusInternalServerError, gin.H{ 30 | "code": 1100, 31 | "msg": "Token expire", 32 | }) 33 | ctx.Abort() 34 | } else { 35 | if (tokenNow != nil && tokenNow.Del != nil && *tokenNow.Del) || tokenNow == nil { 36 | ctx.JSON(http.StatusInternalServerError, gin.H{ 37 | "code": 1100, 38 | "msg": "Token expire", 39 | }) 40 | ctx.Abort() 41 | } 42 | } 43 | 44 | ctx.Set(constants.GIN_USER_ID, *tokenNow.Uid) 45 | } 46 | 47 | // 異常處理 48 | func Recover(c *gin.Context) { 49 | c.Writer.Header().Add("Access-Control-Allow-Origin", "*") 50 | c.Writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS") 51 | c.Writer.Header().Add("Access-Control-Allow-Headers", "*") 52 | lang := c.GetHeader("Accept-Language") 53 | c.Set(constants.GIN_LANG, lang) 54 | // 加载defer异常处理 55 | defer func() { 56 | if err := recover(); err != nil { 57 | c.Writer.WriteHeader(http.StatusInternalServerError) 58 | log.Printf("Error:%s", err) 59 | // 返回统一的Json风格 60 | var msgStr string 61 | if fmt.Sprint(reflect.TypeOf(err)) == "string" { 62 | msgStr = fmt.Sprint(err) 63 | } else { 64 | msgStr = "system error" 65 | } 66 | c.JSON(http.StatusInternalServerError, gin.H{ 67 | "code": 500, 68 | "msg": msgStr, 69 | "success": false, 70 | }) 71 | //终止后续操作 72 | c.Abort() 73 | } 74 | }() 75 | if c.Request.Method == "OPTIONS" { 76 | c.Writer.WriteHeader(http.StatusNoContent) 77 | c.Abort() 78 | // return 79 | } 80 | //继续操作 81 | c.Next() 82 | } 83 | -------------------------------------------------------------------------------- /model/app.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type App struct { 4 | Id *int `gorm:"primarykey;autoIncrement;size:32"` 5 | Uid *int `json:"uid"` 6 | AppName *string `json:"appName"` 7 | OS *int `json:"os"` 8 | CreateTime *int64 `json:"createTime"` 9 | } 10 | 11 | func (App) GetAppByUidAndAppName(uid int, appName string) *App { 12 | var app *App 13 | err := userDb.Where("uid", uid).Where("app_name", appName).First(&app).Error 14 | if err != nil { 15 | return nil 16 | } 17 | return app 18 | } 19 | -------------------------------------------------------------------------------- /model/base.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "com.lc.go.codepush/server/db" 5 | "com.lc.go.codepush/server/model/constants" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | var userDb, _ = db.GetUserDB() 10 | 11 | func queryPage[T any](db *gorm.DB, page constants.PageBean) *constants.PageData[T] { 12 | var datas []T 13 | var total int64 14 | db.Count(&total) 15 | err := db.Offset(page.Page * page.Rows).Limit(page.Rows).Find(&datas).Error 16 | if err != nil { 17 | return nil 18 | } 19 | return &constants.PageData[T]{ 20 | TotalCount: total, 21 | Data: datas, 22 | } 23 | } 24 | 25 | func Update[T any](saveData *T) { 26 | userDb.Updates(&saveData) 27 | } 28 | 29 | func Create[T any](saveData *T) (err error) { 30 | tx := userDb.Create(&saveData) 31 | return tx.Error 32 | } 33 | 34 | func GetOne[T any](sql string, arg any) *T { 35 | var t *T 36 | err := userDb.Select("*").Where(sql, arg).First(&t).Error 37 | if err != nil { 38 | return nil 39 | } 40 | return t 41 | } 42 | 43 | func GetList[T any](sql string, arg any) *[]T { 44 | var t *[]T 45 | err := userDb.Select("*").Where(sql, arg).Find(&t).Error 46 | if err != nil { 47 | return nil 48 | } 49 | return t 50 | } 51 | 52 | func Delete[T any](deleteData T) error { 53 | return userDb.Delete(deleteData).Error 54 | } 55 | 56 | func DeleteWhere(query string, args string, deleteData any) error { 57 | return userDb.Where(query, args).Delete(deleteData).Error 58 | } 59 | -------------------------------------------------------------------------------- /model/constants/constants.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | const ( 4 | GIN_USER_ID = "GIN_USER_ID" 5 | GIN_LANG = "LANG" 6 | ) 7 | const ( 8 | REDIS_TOKEN_INFO = "TOKEN:" 9 | REDIS_UPDATE_INFO = "UPDATE_INFO:" 10 | ) 11 | 12 | const ( 13 | CONFIG_LOGIN_VERIFICATION = "CONFIG_LOGIN_VERIFICATION" 14 | CONFIG_REGISTER_VERIFICATION = "CONFIG_REGISTER_VERIFICATION" 15 | ) 16 | 17 | type ErrObj struct { 18 | Code int `json:"code"` 19 | Msg string `json:"msg"` 20 | } 21 | 22 | type PageData[T any] struct { 23 | Data []T `json:"data"` 24 | TotalCount int64 `json:"totalCount"` 25 | } 26 | 27 | type PageBean struct { 28 | Page int `json:"page"` 29 | Rows int `json:"rows"` 30 | } 31 | 32 | func (PageBean) GetNew() PageBean { 33 | return PageBean{Rows: 10} 34 | } 35 | -------------------------------------------------------------------------------- /model/constants/token_info.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | type TokenInfo struct { 4 | Uid *int `json:"uid"` 5 | UserName *string `json:"userName"` 6 | Token *string `json:"token"` 7 | Money *int64 `json:"money"` 8 | VipTime *int64 `json:"vipTime"` 9 | // PlatformId int 10 | } 11 | -------------------------------------------------------------------------------- /model/deployment.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Deployment struct { 4 | Id *int `gorm:"primarykey;autoIncrement;size:32"` 5 | AppId *int `json:"appId"` 6 | Name *string `json:"name"` 7 | Key *string `json:"key"` 8 | VersionId *int `json:"versionId"` 9 | UpdateTime *int64 `json:"updateTime"` 10 | CreateTime *int64 `json:"createTime"` 11 | } 12 | 13 | func (Deployment) TableName() string { 14 | return "deployment" 15 | } 16 | 17 | func (Deployment) GetByAppidAndName(appId int, name string) *Deployment { 18 | var deployment *Deployment 19 | err := userDb.Where("app_id", appId).Where("name", name).First(&deployment).Error 20 | if err != nil { 21 | return nil 22 | } 23 | return deployment 24 | } 25 | 26 | func (Deployment) GetByAppids(appId int) *[]Deployment { 27 | var deployment *[]Deployment 28 | err := userDb.Where("app_id", appId).Find(&deployment).Error 29 | if err != nil { 30 | return nil 31 | } 32 | return deployment 33 | } 34 | -------------------------------------------------------------------------------- /model/deploymentVersion.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DeploymentVersion struct { 4 | Id *int `gorm:"primarykey;autoIncrement;size:32"` 5 | DeploymentId *int `json:"deploymentId"` 6 | AppVersion *string `json:"appVersion"` 7 | VersionNum *int64 `json:"version_num"` 8 | CurrentPackage *int `json:"currentPackage"` 9 | UpdateTime *int64 `json:"updateTime"` 10 | CreateTime *int64 `json:"createTime"` 11 | } 12 | 13 | func (DeploymentVersion) TableName() string { 14 | return "deployment_version" 15 | } 16 | 17 | func (DeploymentVersion) GetByKeyDeploymentIdAndVersion(deploymentId int, version string) *DeploymentVersion { 18 | var deploymentVersion *DeploymentVersion 19 | err := userDb.Where("deployment_id", deploymentId).Where("app_version", version).First(&deploymentVersion).Error 20 | if err != nil { 21 | return nil 22 | } 23 | return deploymentVersion 24 | } 25 | 26 | func (DeploymentVersion) GetNewVersionByKeyDeploymentId(deploymentId int) *DeploymentVersion { 27 | var deploymentVersion *DeploymentVersion 28 | err := userDb.Where("deployment_id", deploymentId).Order("version_num desc").First(&deploymentVersion).Error 29 | if err != nil { 30 | return nil 31 | } 32 | return deploymentVersion 33 | } 34 | 35 | func (DeploymentVersion) UpdateCurrentPackage(id int, pid *int) { 36 | userDb.Raw("update deployment_version set current_package=? where id=?", pid, id).Scan(&DeploymentVersion{}) 37 | } 38 | -------------------------------------------------------------------------------- /model/package.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Package struct { 4 | Id *int `gorm:"primarykey;autoIncrement;size:32"` 5 | DeploymentId *int `json:"deploymentId"` 6 | DeploymentVersionId *int `json:"deploymentVersionId"` 7 | Size *int64 `json:"size"` 8 | Hash *string `json:"hash"` 9 | Download *string `json:"download"` 10 | Active *int `json:"active"` 11 | Failed *int `json:"failed"` 12 | Installed *int `json:"installed"` 13 | CreateTime *int64 `json:"create_time"` 14 | Description *string `json:"description"` 15 | } 16 | 17 | func (Package) TableName() string { 18 | return "package" 19 | } 20 | 21 | func (Package) AddActive(pid int) { 22 | userDb.Raw("update package set active=active+1 where id=?", pid).Scan(&Package{}) 23 | } 24 | 25 | func (Package) AddFailed(pid int) { 26 | userDb.Raw("update package set failed=failed+1 where id=?", pid).Scan(&Package{}) 27 | } 28 | 29 | func (Package) AddInstalled(pid int) { 30 | userDb.Raw("update package set installed=installed+1 where id=?", pid).Scan(&Package{}) 31 | } 32 | 33 | func (Package) GetRollbackPack(deploymentId int, lastPakcId int, deploymentVersionId int) *Package { 34 | var lastPackage *Package 35 | err := userDb.Where("deployment_id=?", deploymentId).Where("id 0 { 374 | log.Panic("App exist deployment,Delete the deployment first and then delete the app ") 375 | } 376 | model.Delete[model.App](model.App{Id: app.Id}) 377 | ctx.JSON(http.StatusOK, gin.H{ 378 | "success": true, 379 | }) 380 | } else { 381 | log.Panic(err.Error()) 382 | } 383 | } 384 | 385 | type delDeploymentInfo struct { 386 | AppName *string `json:"appName" binding:"required"` 387 | Deployment *string `json:"deployment" binding:"required"` 388 | } 389 | 390 | func (App) DelDeployment(ctx *gin.Context) { 391 | delDeploymentInfo := delDeploymentInfo{} 392 | if err := ctx.ShouldBindBodyWith(&delDeploymentInfo, binding.JSON); err == nil { 393 | uid := ctx.MustGet(constants.GIN_USER_ID).(int) 394 | 395 | app := model.App{}.GetAppByUidAndAppName(uid, *delDeploymentInfo.AppName) 396 | if app == nil { 397 | log.Panic("App not found") 398 | } 399 | deployment := model.Deployment{}.GetByAppidAndName(*app.Id, *delDeploymentInfo.Deployment) 400 | if deployment == nil { 401 | log.Panic("Deployment " + *delDeploymentInfo.Deployment + " not found") 402 | } 403 | userDb, _ := db.GetUserDB() 404 | err := userDb.Transaction(func(tx *gorm.DB) error { 405 | if err := tx.Delete(model.Deployment{Id: deployment.Id}).Error; err != nil { 406 | panic("DeleteError:" + err.Error()) 407 | } 408 | if err := tx.Where("deployment_id", *deployment.Id).Delete(model.DeploymentVersion{}).Error; err != nil { 409 | panic("DeleteError:" + err.Error()) 410 | } 411 | if err := tx.Where("deployment_id", *deployment.Id).Delete(model.Package{}).Error; err != nil { 412 | panic("DeleteError:" + err.Error()) 413 | } 414 | return nil 415 | }) 416 | if err != nil { 417 | panic("DeleteError:" + err.Error()) 418 | } 419 | 420 | ctx.JSON(http.StatusOK, gin.H{ 421 | "success": true, 422 | }) 423 | } else { 424 | log.Panic(err.Error()) 425 | } 426 | } 427 | 428 | type rollbackReq struct { 429 | AppName *string `json:"appName" binding:"required"` 430 | Deployment *string `json:"deployment" binding:"required"` 431 | Version *string `json:"version"` 432 | } 433 | 434 | func (App) Rollback(ctx *gin.Context) { 435 | rollbackReq := rollbackReq{} 436 | if err := ctx.ShouldBindBodyWith(&rollbackReq, binding.JSON); err == nil { 437 | uid := ctx.MustGet(constants.GIN_USER_ID).(int) 438 | 439 | app := model.App{}.GetAppByUidAndAppName(uid, *rollbackReq.AppName) 440 | if app == nil { 441 | log.Panic("App not found") 442 | } 443 | deployment := model.Deployment{}.GetByAppidAndName(*app.Id, *rollbackReq.Deployment) 444 | if deployment == nil { 445 | log.Panic("Deployment " + *rollbackReq.Deployment + " not found") 446 | } 447 | 448 | var deploymentVersion *model.DeploymentVersion 449 | if deployment.VersionId != nil { 450 | deploymentVersion = model.DeploymentVersion{}.GetByKeyDeploymentIdAndVersion(*deployment.Id, *rollbackReq.Version) 451 | } 452 | if deploymentVersion == nil { 453 | log.Panic("Version not found") 454 | } 455 | if deploymentVersion.CurrentPackage == nil { 456 | log.Panic("There is no upload package for the current version") 457 | } 458 | newPackage := model.Package{}.GetRollbackPack(*deployment.Id, *deploymentVersion.CurrentPackage, *deploymentVersion.Id) 459 | 460 | userDb, _ := db.GetUserDB() 461 | err := userDb.Transaction(func(tx *gorm.DB) error { 462 | if newPackage == nil { 463 | model.DeploymentVersion{}.UpdateCurrentPackage(*deploymentVersion.Id, nil) 464 | } else { 465 | model.DeploymentVersion{}.UpdateCurrentPackage(*deploymentVersion.Id, newPackage.Id) 466 | } 467 | return nil 468 | }) 469 | if err != nil { 470 | panic("RollbackError:" + err.Error()) 471 | } 472 | 473 | if newPackage != nil { 474 | ctx.JSON(http.StatusOK, gin.H{ 475 | "Success": true, 476 | "Version": *deploymentVersion.AppVersion, 477 | "PackId": *newPackage.Id, 478 | "Size": *newPackage.Size, 479 | "Hash": *newPackage.Hash, 480 | "CreateTime": *newPackage.CreateTime, 481 | }) 482 | } else { 483 | ctx.JSON(http.StatusOK, gin.H{ 484 | "Success": true, 485 | "Version": *deploymentVersion.AppVersion, 486 | }) 487 | } 488 | redis.DelRedisObj(constants.REDIS_UPDATE_INFO + *deployment.Key + "*") 489 | } else { 490 | log.Panic(err.Error()) 491 | } 492 | } 493 | -------------------------------------------------------------------------------- /request/client.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "strconv" 7 | 8 | "com.lc.go.codepush/server/config" 9 | "com.lc.go.codepush/server/db/redis" 10 | "com.lc.go.codepush/server/model" 11 | "com.lc.go.codepush/server/model/constants" 12 | "com.lc.go.codepush/server/utils" 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | type Client struct{} 17 | type updateInfo struct { 18 | DownloadUrl string `json:"download_url"` 19 | Description string `json:"description"` 20 | IsAvailable bool `json:"is_available"` 21 | IsDisabled bool `json:"is_disabled"` 22 | TargetBinaryRange string `json:"target_binary_range"` 23 | PackageHash string `json:"package_hash"` 24 | Label string `json:"label"` 25 | PackageSize int64 `json:"package_size"` 26 | UpdateAppVersion bool `json:"update_app_version"` 27 | ShouldRunBinaryVersion bool `json:"should_run_binary_version"` 28 | IsMandatory bool `json:"is_mandatory"` 29 | } 30 | type updateInfoRedisInfo struct { 31 | updateInfo 32 | NewVersion string 33 | } 34 | 35 | func (Client) CheckUpdate(ctx *gin.Context) { 36 | deploymentKey := ctx.Query("deployment_key") 37 | appVersion := ctx.Query("app_version") 38 | packageHash := ctx.Query("package_hash") 39 | // label := ctx.Query("label") 40 | // clientUniqueId := ctx.Query("client_unique_id") 41 | redisKey := constants.REDIS_UPDATE_INFO + deploymentKey + ":" + appVersion 42 | updateInfoRedis := redis.GetRedisObj[updateInfoRedisInfo](redisKey) 43 | updateInfo := updateInfo{} 44 | config := config.GetConfig() 45 | 46 | if updateInfoRedis == nil { 47 | updateInfoRedis = &updateInfoRedisInfo{} 48 | deployment := model.GetOne[model.Deployment]("key", deploymentKey) 49 | if deployment == nil { 50 | log.Panic("Key error") 51 | } 52 | deploymentVersion := model.DeploymentVersion{}.GetByKeyDeploymentIdAndVersion(*deployment.Id, appVersion) 53 | if deploymentVersion != nil { 54 | if deploymentVersion.CurrentPackage != nil { 55 | packag := model.GetOne[model.Package]("id", deploymentVersion.CurrentPackage) 56 | if packag != nil { 57 | // && *packag.Hash != packageHash 58 | updateInfoRedis.TargetBinaryRange = *deploymentVersion.AppVersion 59 | updateInfoRedis.PackageHash = *packag.Hash 60 | updateInfoRedis.PackageSize = *packag.Size 61 | updateInfoRedis.IsAvailable = true 62 | updateInfoRedis.IsMandatory = true 63 | label := strconv.Itoa(*packag.Id) 64 | updateInfoRedis.Label = label 65 | updateInfoRedis.DownloadUrl = config.ResourceUrl + *packag.Download 66 | if packag.Description != nil { 67 | updateInfoRedis.Description = *packag.Description 68 | } 69 | } 70 | } 71 | } 72 | deploymentVersionNew := model.DeploymentVersion{}.GetNewVersionByKeyDeploymentId(*deployment.Id) 73 | if deploymentVersionNew != nil { 74 | updateInfoRedis.NewVersion = *deploymentVersionNew.AppVersion 75 | } 76 | redis.SetRedisObj(redisKey, updateInfoRedis, -1) 77 | } 78 | if updateInfoRedis.PackageHash != "" { 79 | if updateInfoRedis.PackageHash != packageHash && appVersion == updateInfoRedis.TargetBinaryRange { 80 | updateInfo.TargetBinaryRange = updateInfoRedis.TargetBinaryRange 81 | updateInfo.PackageHash = updateInfoRedis.PackageHash 82 | updateInfo.PackageSize = updateInfoRedis.PackageSize 83 | updateInfo.IsAvailable = true 84 | updateInfo.IsMandatory = true 85 | updateInfo.Label = updateInfoRedis.Label 86 | updateInfo.DownloadUrl = updateInfoRedis.DownloadUrl 87 | updateInfo.Description = updateInfoRedis.Description 88 | } else if updateInfoRedis.NewVersion != "" && appVersion != updateInfoRedis.NewVersion && utils.FormatVersionStr(appVersion) < utils.FormatVersionStr(updateInfoRedis.NewVersion) { 89 | updateInfo.TargetBinaryRange = updateInfoRedis.NewVersion 90 | updateInfo.UpdateAppVersion = true 91 | } 92 | 93 | } else if updateInfoRedis.NewVersion != "" && appVersion != updateInfoRedis.NewVersion && utils.FormatVersionStr(appVersion) < utils.FormatVersionStr(updateInfoRedis.NewVersion) { 94 | updateInfo.TargetBinaryRange = updateInfoRedis.NewVersion 95 | updateInfo.UpdateAppVersion = true 96 | } 97 | 98 | ctx.JSON(http.StatusOK, gin.H{ 99 | "update_info": updateInfo, 100 | }) 101 | 102 | } 103 | 104 | type reportStatuReq struct { 105 | AppVersion *string `json:"app_version"` 106 | DeploymentKey *string `json:"deployment_key"` 107 | ClientUniqueId *string `json:"client_unique_id"` 108 | Label *string `json:"label"` 109 | Status *string `json:"status"` 110 | PreviousLabelOrAppVersion *string `json:"previous_label_or_app_version"` 111 | PreviousDeploymentKey *string `json:"previous_deployment_key"` 112 | } 113 | 114 | func (Client) ReportStatus(ctx *gin.Context) { 115 | json := reportStatuReq{} 116 | ctx.BindJSON(&json) 117 | if json.Status != nil { 118 | pack := model.GetOne[model.Package]("id=?", json.Label) 119 | if pack != nil { 120 | if *json.Status == "DeploymentSucceeded" { 121 | model.Package{}.AddActive(*pack.Id) 122 | } else if *json.Status == "DeploymentFailed" { 123 | model.Package{}.AddFailed(*pack.Id) 124 | } 125 | } 126 | 127 | } 128 | 129 | ctx.String(http.StatusOK, "OK") 130 | } 131 | 132 | type downloadReq struct { 133 | ClientUniqueId *string `json:"client_unique_id"` 134 | DeploymentKey *string `json:"deployment_key"` 135 | Label *string `json:"label"` 136 | } 137 | 138 | func (Client) Download(ctx *gin.Context) { 139 | json := downloadReq{} 140 | ctx.BindJSON(&json) 141 | pack := model.GetOne[model.Package]("id=?", json.Label) 142 | if pack != nil { 143 | model.Package{}.AddInstalled(*pack.Id) 144 | } 145 | ctx.String(http.StatusOK, "OK") 146 | } 147 | -------------------------------------------------------------------------------- /request/user.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "com.lc.go.codepush/server/config" 8 | "com.lc.go.codepush/server/model" 9 | "com.lc.go.codepush/server/model/constants" 10 | "com.lc.go.codepush/server/utils" 11 | "github.com/gin-gonic/gin" 12 | "github.com/gin-gonic/gin/binding" 13 | "github.com/google/uuid" 14 | ) 15 | 16 | type User struct{} 17 | 18 | type loginUser struct { 19 | UserName *string `json:"userName" binding:"required"` 20 | Password *string `json:"password" binding:"required"` 21 | } 22 | 23 | func (User) Login(ctx *gin.Context) { 24 | loginUser := loginUser{} 25 | if err := ctx.ShouldBindBodyWith(&loginUser, binding.JSON); err == nil { 26 | user := model.GetOne[model.User]("user_name", &loginUser.UserName) 27 | if user == nil || *user.Password != *loginUser.Password { 28 | panic("UserName or Psssword error") 29 | } 30 | uuid, _ := uuid.NewUUID() 31 | timeNow := utils.GetTimeNow() 32 | expireTime := *timeNow + (config.GetConfig().TokenExpireTime * 24 * 60 * 60 * 1000) 33 | token := uuid.String() 34 | del := false 35 | tokenInfo := model.Token{ 36 | Uid: user.Id, 37 | Token: &token, 38 | ExpireTime: &expireTime, 39 | Del: &del, 40 | } 41 | err := model.Create[model.Token](&tokenInfo) 42 | if err != nil { 43 | panic("create token error") 44 | } 45 | ctx.JSON(http.StatusOK, gin.H{ 46 | "token": token, 47 | }) 48 | } else { 49 | log.Panic(err.Error()) 50 | } 51 | } 52 | 53 | type changePasswordReq struct { 54 | Password *string `json:"password" binding:"required"` 55 | } 56 | 57 | func (User) ChangePassword(ctx *gin.Context) { 58 | req := changePasswordReq{} 59 | if err := ctx.ShouldBindBodyWith(&req, binding.JSON); err == nil { 60 | uid := ctx.MustGet(constants.GIN_USER_ID).(int) 61 | err := model.User{}.ChangePassword(uid, *req.Password) 62 | if err != nil { 63 | panic(err.Error()) 64 | } 65 | ctx.JSON(http.StatusOK, gin.H{ 66 | "success": true, 67 | }) 68 | } else { 69 | log.Panic(err.Error()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "sort" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func GetTimeNow() *int64 { 13 | t := time.Now().UnixMilli() 14 | return &t 15 | } 16 | 17 | func CreateInt(num int) *int { 18 | return &num 19 | } 20 | 21 | func Exists(path string) bool { 22 | _, err := os.Stat(path) //os.Stat获取文件信息 23 | if err != nil { 24 | return os.IsExist(err) 25 | } 26 | return true 27 | } 28 | 29 | func FormatVersionStr(v string) int64 { 30 | vs := strings.Split(v, ".") 31 | if len(vs) <= 0 { 32 | log.Panic("Version str error") 33 | } 34 | var vNum int64 35 | ReverseArr(vs) 36 | for index, v := range vs { 37 | num, err := strconv.ParseInt(v, 10, 64) 38 | if err != nil { 39 | log.Panic(err.Error()) 40 | } 41 | for i := 0; i < index; i++ { 42 | num = num * 100 43 | } 44 | vNum += num 45 | } 46 | return vNum 47 | } 48 | func ReverseArr(s interface{}) { 49 | sort.SliceStable(s, func(i, j int) bool { 50 | return true 51 | }) 52 | } 53 | --------------------------------------------------------------------------------