🍭 Merge remote-tracking branch 'upstream/main' into docker-compose

pull/70/head
RenChu 3 years ago
commit 5c7c941409

3
.gitignore vendored

@ -1,7 +1,8 @@
.idea
.vscode
!*.example
dist/
config.yaml
*.log
paopao-ce*
release
data

@ -1,17 +1,24 @@
# build app
FROM golang AS build-env
ADD . /paopao-ce
# build frontend
FROM node:18-alpine as frontend
WORKDIR /web
COPY web/ ./
RUN echo 'VITE_HOST=""'>.env && yarn && yarn build
# build backend
FROM golang:1.18-alpine AS backend
RUN apk --no-cache --no-progress add --virtual \
build-deps \
build-base \
git
WORKDIR /paopao-ce
COPY . .
COPY --from=frontend /web/dist ./web/dist
ENV GOPROXY=https://goproxy.cn
RUN make build TAGS='embed'
RUN CGO_ENABLED=0 go build .
# safe image
FROM alpine
FROM alpine:3.16
ENV TZ=Asia/Shanghai
RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates
COPY --from=build-env /paopao-ce/paopao-ce /usr/bin/paopao-ce
@ -22,6 +29,3 @@ COPY config.yaml .
EXPOSE 8000
CMD ["paopao-ce"]
# HEALTHCHECK
HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD ps -ef | grep paopao-ce || exit 1

@ -1,19 +1,66 @@
.PHONY: all build run test clean fmt help
BUILD_VERSION := $(shell cat version)
TARGET = paopao-ce
RELEASE_ROOT = release
RELEASE_FILES = LICENSE README.md config.yaml.sample scripts assets configs
RELEASE_LINUX_AMD64 = $(RELEASE_ROOT)/linux-amd64/$(TARGET)
RELEASE_DARWIN_AMD64 = $(RELEASE_ROOT)/darwin-amd64/$(TARGET)
RELEASE_DARWIN_ARM64 = $(RELEASE_ROOT)/darwin-arm64/$(TARGET)
RELEASE_WINDOWS_AMD64 = $(RELEASE_ROOT)/windows-amd64/$(TARGET)
BUILD_VERSION := $(shell cat version)
BUILD_DATE := $(shell date +'%Y-%m-%d %H:%M:%S')
SHA_SHORT := $(shell git rev-parse --short HEAD)
TAGS = ""
LDFLAGS = -X "main.version=${BUILD_VERSION}" -X "main.buildDate=${BUILD_DATE}" -X "main.commitID=${SHA_SHORT}" -w -s
all: fmt build
build:
@go mod download
@echo Build paopao-ce
bash build.sh paopao-ce
@go build -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_ROOT)/$(TARGET)
run:
@go run -tags '$(TAGS)' -ldflags "-X 'main.version=${BUILD_VERSION}' -X 'main.buildDate=${BUILD_DATE}' -X 'main.commitID=${SHA_SHORT}'" .
@go run -trimpath -gcflags "all=-N -l" -tags '$(TAGS)' -ldflags '$(LDFLAGS)' .
.PHONY: release
release: linux-amd64 darwin-amd64 darwin-arm64 windows-x64
@echo Package paopao-ce
@cp -rf $(RELEASE_FILES) $(RELEASE_LINUX_AMD64)
@cp -rf $(RELEASE_FILES) $(RELEASE_DARWIN_AMD64)
@cp -rf $(RELEASE_FILES) $(RELEASE_DARWIN_ARM64)
@cp -rf $(RELEASE_FILES) $(RELEASE_WINDOWS_AMD64)
@cd $(RELEASE_LINUX_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-linux_amd64.zip $(TARGET) && cd -
@cd $(RELEASE_DARWIN_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-darwin_amd64.zip $(TARGET) && cd -
@cd $(RELEASE_DARWIN_ARM64)/.. && rm -f *.zip && zip -r $(TARGET)-darwin_arm64.zip $(TARGET) && cd -
@cd $(RELEASE_WINDOWS_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-windows_amd64.zip $(TARGET) && cd -
.PHONY: linux-amd64
linux-amd64:
@echo Build paopao-ce [linux-amd64]
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_LINUX_AMD64)/$(TARGET)
.PHONY: darwin-amd64
darwin-amd64:
@echo Build paopao-ce [darwin-amd64]
@CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_DARWIN_AMD64)/$(TARGET)
.PHONY: darwin-arm64
darwin-arm64:
@echo Build paopao-ce [darwin-arm64]
@CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_DARWIN_ARM64)/$(TARGET)
.PHONY: windows-x64
windows-x64:
@echo Build paopao-ce [windows-x64]
@CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_WINDOWS_AMD64)/$(TARGET).exe
clean:
@go clean
@find ./dist -type f -exec rm -r {} +
@find ./tmp -type f -exec rm -r {} +
@find ./release -type f -exec rm -r {} +
fmt:
@echo Formatting...
@go fmt ./global/...
@ -22,14 +69,15 @@ fmt:
@go vet -composites=false ./global/...
@go vet -composites=false ./internal/...
@go vet -composites=false ./pkg/...
test:
@go test ./...
help:
@echo "make: make"
@echo "make run: start api server"
@echo "make build: build executables"
@echo "make build: build executables"
@echo "make build: build executable"
@echo "make release: build release executables"
@echo "make run TAGS='embed': start api server and serve embed web frontend"
@echo "make build TAGS='embed': build executables with embed web frontend"
.EXPORT_ALL_VARIABLES:
GO111MODULE = on
@echo "make build TAGS='embed': build executable with embed web frontend"
@echo "make release TAGS='embed': build release executables with embed web frontend"

@ -84,20 +84,23 @@ PaoPao主要由以下优秀的开源项目/工具构建
#### 后端
1. 导入项目根目录下的 `paopao.sql` 文件至MySQL数据库
1. 导入项目根目录下的 `scripts/paopao.sql` 文件至MySQL数据库
2. 拷贝项目根目录下 `config.yaml.sample` 文件至 `config.yaml`,按照注释完成配置编辑
3. 编译后端
编译api服务:
```sh
make build
```
编译api服务、内嵌web前端ui; 注意此步骤需要先编译web前端。
编译api服务、内嵌web前端ui:
```sh
make build TAGS='embed'
```
编译后在`dist`目录可以找到对应可执行文件。
编译后在`release`目录可以找到对应可执行文件。
```sh
release/paopao-ce
```
4. 启动后端
4. 直接运行后端
运行api服务:
```sh
make run
@ -106,6 +109,7 @@ PaoPao主要由以下优秀的开源项目/工具构建
```sh
make run TAGS='embed'
```
提示: 如果需要内嵌web前端ui请先构建web前端(建议设置web/.env为VITE_HOST="")。
#### 前端
@ -148,6 +152,35 @@ PaoPao主要由以下优秀的开源项目/工具构建
桌面端是使用[Rust](https://www.rust-lang.org/) + [tauri](https://github.com/tauri-apps/tauri)编写
需要安装tauri的依赖具体参考[https://tauri.studio/v1/guides/getting-started/prerequisites](https://tauri.studio/v1/guides/getting-started/prerequisites).
### docker-compose 运行
```sh
%> git clone https://github.com/rocboss/paopao-ce.git
%> docker compose up --build
# visit http://localhost:8008
```
默认是使用config.yaml.sample的配置如果需要自定义配置请拷贝默认配置文件(比如config.yaml)修改后再同步配置到docker-compose.yaml如下
```
# file: docker-compose.yaml
...
backend:
build:
context: .
restart: always
depends_on:
- db
- redis
- zinc
# modify below to reflect your custom configure
volumes:
- ./config.yaml:/app/paopao-ce/config.yaml
ports:
- 127.0.0.1:8008:8008
networks:
- paopao-network
....
```
### 其他说明
建议后端服务使用 `supervisor` 守护进程,并通过 `nginx` 反向代理后提供API给前端服务调用。

@ -1,35 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2155
set -e
DIST_PREFIX=${1}
DEBUG_MODE=${2}
TARGET_DIR="dist"
PLATFORMS="darwin/amd64 darwin/arm64 linux/amd64 windows/amd64"
rm -rf ${TARGET_DIR}
mkdir ${TARGET_DIR}
for pl in ${PLATFORMS}; do
export CGO_ENABLED=0
export GOOS=$(echo "${pl}" | cut -d'/' -f1)
export GOARCH=$(echo "${pl}" | cut -d'/' -f2)
export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}
if [ "${GOOS}" == "windows" ]; then
export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}.exe
fi
echo "build => ${TARGET}"
if [ "${DEBUG_MODE}" == "debug" ]; then
go build -trimpath -gcflags "all=-N -l" -o "${TARGET}" -tags "${TAGS}" \
-ldflags "-X 'main.version=${BUILD_VERSION}' \
-X 'main.buildDate=${BUILD_DATE}' \
-X 'main.commitID=${SHA_SHORT}'\
-w -s"
else
go build -trimpath -o "${TARGET}" -tags "${TAGS}" \
-ldflags "-X 'main.version=${BUILD_VERSION}' \
-X 'main.buildDate=${BUILD_DATE}' \
-X 'main.commitID=${SHA_SHORT}'\
-w -s"
fi
done

@ -12,8 +12,8 @@ Server: # 服务设置
ReadTimeout: 60
WriteTimeout: 60
Features:
Default: ["Sms", "Alipay", "Zinc", "MySQL", "Redis", "AliOSS", "LoggerZinc"]
Develop: ["Zinc", "MySQL", "AliOSS", "LoggerFile"]
Default: ["Alipay", "Zinc", "MySQL", "Redis", "AliOSS", "LoggerZinc"]
Develop: ["Sms", "Zinc", "MySQL", "AliOSS", "LoggerFile"]
Slim: ["Zinc", "MySQL", "Redis", "AliOSS", "LoggerFile"]
Sms: "SmsJuhe"
SmsJuhe:
@ -24,11 +24,11 @@ Alipay:
AppID:
PrivateKey:
LoggerFile: # 使用File写日志
SavePath: storage/logs
SavePath: data/paopao-ce/logs
FileName: app
FileExt: .log
LoggerZinc: # 使用Zinc写日志
Host: http://127.0.0.1:4080/es/_bulk
Host: http://zinc:4080/es/_bulk
Index: paopao-log
User: admin
Password: admin
@ -36,13 +36,8 @@ JWT: # 鉴权加密
Secret: 18a6413dc4fe394c66345ebe501b2f26
Issuer: paopao-api
Expire: 86400
Search: # 搜索配置
ZincHost: http://127.0.0.1:4080
ZincIndex: paopao-data
ZincUser: admin
ZincPassword: admin
Zinc: # Zinc搜索配置
Host: http://127.0.0.1:4080
Host: http://zinc:4080
Index: paopao-data
User: admin
Password: admin
@ -53,9 +48,9 @@ AliOSS: # 阿里云OSS存储配置
Bucket:
Domain:
MySQL: # MySQL数据库
Username: root # 填写你的数据库账号
Password: root # 填写你的数据库密码
Host: 127.0.0.1:3306
Username: paopao
Password: paopao
Host: db:3306
DBName: paopao
TablePrefix: p_
Charset: utf8mb4
@ -64,6 +59,7 @@ MySQL: # MySQL数据库
MaxIdleConns: 10
MaxOpenConns: 30
Redis:
Host: 127.0.0.1:6379
Host: redis:6379
Password:
DB:
DB:

@ -1,7 +1,9 @@
package main
import (
"flag"
"log"
"strings"
"sync"
"time"
@ -14,6 +16,24 @@ import (
"github.com/rocboss/paopao-ce/pkg/zinc"
)
var (
noDefaultFeatures bool
features suites
)
type suites []string
func (s *suites) String() string {
return strings.Join(*s, ",")
}
func (s *suites) Set(value string) error {
for _, item := range strings.Split(value, ",") {
*s = append(*s, strings.TrimSpace(item))
}
return nil
}
func init() {
const RESTARTS = 5
const SECONDS = 10
@ -78,6 +98,11 @@ func setupSetting() error {
}
global.Features = setting.FeaturesFrom("Features")
if len(features) > 0 {
if err = global.Features.Use(features, noDefaultFeatures); err != nil {
return err
}
}
objects := map[string]interface{}{
"App": &global.AppSetting,
@ -103,6 +128,12 @@ func setupSetting() error {
return nil
}
func flagParse() {
flag.BoolVar(&noDefaultFeatures, "no-default-features", false, "whether use default features")
flag.Var(&features, "features", "use special features")
flag.Parse()
}
func setupLogger() error {
logger, err := logger.New()
if err != nil {

@ -33,6 +33,79 @@ func TestUseDefault(t *testing.T) {
"Sms": true,
"Sms = SmsJuhe": true,
"SmsJuhe": false,
"default": true,
} {
if ok := features.CfgIf(exp); res != ok {
t.Errorf("CfgIf(%s) want %t got %t", exp, res, ok)
}
}
}
func TestUse(t *testing.T) {
suites := map[string][]string{
"default": {"Sms", "Alipay", "Zinc", "MySQL", "Redis", "AliOSS", "LogZinc"},
"develop": {"Zinc", "MySQL", "AliOSS", "LogFile"},
"slim": {"Zinc", "MySQL", "Redis", "AliOSS", "LogFile"},
}
kv := map[string]string{
"sms": "SmsJuhe",
}
features := newFeatures(suites, kv)
features.Use([]string{"develop"}, true)
for _, data := range []struct {
key string
expect string
exist bool
}{
{"Sms", "", false},
{"Alipay", "", false},
{"Zinc", "", true},
{"Redis", "", false},
{"Database", "", false},
} {
if v, ok := features.Cfg(data.key); ok != data.exist || v != data.expect {
t.Errorf("key: %s expect: %s exist: %t got v: %s ok: %t", data.key, data.expect, data.exist, v, ok)
}
}
for exp, res := range map[string]bool{
"Sms": false,
"Sms = SmsJuhe": false,
"SmsJuhe": false,
"default": false,
"develop": true,
} {
if ok := features.CfgIf(exp); res != ok {
t.Errorf("CfgIf(%s) want %t got %t", exp, res, ok)
}
}
features.UseDefault()
features.Use([]string{"slim", "", "demo"}, false)
for _, data := range []struct {
key string
expect string
exist bool
}{
{"Sms", "SmsJuhe", true},
{"Alipay", "", true},
{"Zinc", "", true},
{"Redis", "", true},
{"Database", "", false},
{"demo", "", true},
} {
if v, ok := features.Cfg(data.key); ok != data.exist || v != data.expect {
t.Errorf("key: %s expect: %s exist: %t got v: %s ok: %t", data.key, data.expect, data.exist, v, ok)
}
}
for exp, res := range map[string]bool{
"Sms": true,
"Sms = SmsJuhe": true,
"SmsJuhe": false,
"default": true,
"develop": false,
"slim": true,
"demo": true,
} {
if ok := features.CfgIf(exp); res != ok {
t.Errorf("CfgIf(%s) want %t got %t", exp, res, ok)

@ -12,11 +12,6 @@ type Setting struct {
vp *viper.Viper
}
type LogType string
const LogFileType LogType = "file"
const LogZincType LogType = "zinc"
type LoggerFileSettingS struct {
SavePath string
FileName string
@ -30,17 +25,6 @@ type LoggerZincSettingS struct {
Password string
}
type LoggerSettingS struct {
LogType LogType
LogFileSavePath string
LogFileName string
LogFileExt string
LogZincHost string
LogZincIndex string
LogZincUser string
LogZincPassword string
}
type ServerSettingS struct {
RunMode string
HttpIp string
@ -84,20 +68,6 @@ type ZincSettingS struct {
Password string
}
type DatabaseSettingS struct {
DBType string
UserName string
Password string
Host string
DBName string
TablePrefix string
Charset string
ParseTime bool
LogLevel logger.LogLevel
MaxIdleConns int
MaxOpenConns int
}
type MySQLSettingS struct {
UserName string
Password string
@ -192,25 +162,28 @@ func (s *Setting) FeaturesFrom(k string) *FeaturesSettingS {
func newFeatures(suites map[string][]string, kv map[string]string) *FeaturesSettingS {
features := &FeaturesSettingS{
suites: suites,
kv: kv,
suites: suites,
kv: kv,
features: make(map[string]string),
}
features.UseDefault()
return features
}
func (f *FeaturesSettingS) UseDefault() {
defaultSuite := f.suites["default"]
f.Use(defaultSuite, true)
f.Use([]string{"default"}, true)
}
func (f *FeaturesSettingS) Use(suite []string, noDefault bool) error {
if noDefault {
if noDefault && len(f.features) != 0 {
f.features = make(map[string]string)
}
features := f.flatFeatures(suite)
for _, feature := range features {
feature = strings.ToLower(feature)
if len(feature) == 0 {
continue
}
f.features[feature] = f.kv[feature]
}
return nil

@ -0,0 +1 @@
All files in subdirectories are templates, do modifications based on your environment first.

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>info.paopao.web</string>
<!-- assumes running under 'paopao' account -->
<!-- modify below to reflect your settings -->
<key>UserName</key>
<string>paopao</string>
<key>GroupName</key>
<string>paopao</string>
<key>ProgramArguments</key>
<array>
<!-- assumes installed in /Users/paopao/app -->
<!-- modify below to reflect your settings -->
<string>/Users/paopao/app/paopao-ce</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<!-- assumes Gogs is installed in /Users/git/gogs -->
<!-- modify below to reflect your settings -->
<key>WorkingDirectory</key>
<string>/Users/paopao/app/</string>
<key>StandardOutPath</key>
<string>/Users/paopao/app/log/stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/paopao/app/log/stderr.log</string>
</dict>
</plist>

@ -0,0 +1,17 @@
[Unit]
Description=paopao-ce
After=syslog.target
After=network.target
After=mariadb.service mysqld.service redis.service
[Service]
Type=simple
User=paopao
Group=paopao
WorkingDirectory=/home/paopao/app
ExecStart=/home/paopao/app/paopao-ce
Restart=always
Environment=USER=paopao HOME=/home/paopao
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save