From 0b66fd838f9aafb1f2a983fc8ef31ab423dca88a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tiger=20Wang=20=28=E7=8E=8B=E8=B1=AB=29?=
 <tigerwang@outlook.com>
Date: Fri, 12 Aug 2022 00:07:50 -0400
Subject: [PATCH] add migration from versions 0.3.3, 0.3.4, 0.3.5 (#3)

---
 .github/workflows/release.yml                 |  8 ++
 .goreleaser.yaml                              | 71 +++++++++++++-
 ...-service.conf => user-service.conf.sample} |  0
 .../script.d/01-migrate-user-service.sh       |  9 ++
 .../service.d/user-service/from-0.2.sh        |  0
 .../service.d/user-service/from-0.3.4.sh      |  1 +
 .../service.d/user-service/from-0.3.sh        |  0
 .../setup/script.d/01-setup-user-service.sh   |  4 +
 .../debian/bullseye/setup-user-service.sh     |  1 +
 .../user-service/debian/setup-user-service.sh |  0
 .../ubuntu/jammy/setup-user-service.sh        |  1 +
 .../user-service/ubuntu/setup-user-service.sh |  1 +
 cmd/migration-tool/log.go                     | 37 +++++++
 cmd/migration-tool/main.go                    | 90 +++++++++++++++++
 cmd/migration-tool/migration-033-034-035.go   | 97 +++++++++++++++++++
 common/version.go                             |  3 +
 go.mod                                        |  6 +-
 go.sum                                        | 12 ++-
 pkg/config/config.go                          | 12 +--
 pkg/config/init.go                            | 29 +++++-
 20 files changed, 358 insertions(+), 24 deletions(-)
 rename build/etc/casaos/{user-service.conf => user-service.conf.sample} (100%)
 create mode 100644 build/usr/share/casaos/migration/script.d/01-migrate-user-service.sh
 create mode 100644 build/usr/share/casaos/migration/service.d/user-service/from-0.2.sh
 create mode 120000 build/usr/share/casaos/migration/service.d/user-service/from-0.3.4.sh
 create mode 100644 build/usr/share/casaos/migration/service.d/user-service/from-0.3.sh
 create mode 100644 build/usr/share/casaos/setup/script.d/01-setup-user-service.sh
 create mode 120000 build/usr/share/casaos/setup/service.d/user-service/debian/bullseye/setup-user-service.sh
 create mode 100644 build/usr/share/casaos/setup/service.d/user-service/debian/setup-user-service.sh
 create mode 120000 build/usr/share/casaos/setup/service.d/user-service/ubuntu/jammy/setup-user-service.sh
 create mode 120000 build/usr/share/casaos/setup/service.d/user-service/ubuntu/setup-user-service.sh
 create mode 100644 cmd/migration-tool/log.go
 create mode 100644 cmd/migration-tool/main.go
 create mode 100644 cmd/migration-tool/migration-033-034-035.go
 create mode 100644 common/version.go

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8b1f8f7..34cfef9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,6 +12,14 @@ jobs:
   goreleaser:
     runs-on: ubuntu-latest
     steps:
+      -
+        name: Install dependencies for cross-compiling
+        run: |
+          sudo apt update 
+          sudo apt-get --no-install-recommends --yes install \
+            libc6-dev-amd64-cross \
+            gcc-10-aarch64-linux-gnu libc6-dev-arm64-cross \
+            gcc-10-arm-linux-gnueabihf libc6-dev-armhf-cross
       -
         name: Checkout
         uses: actions/checkout@v2
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index 8a57e2b..67768d0 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -6,19 +6,84 @@ before:
     # You may remove this if you don't use go modules.
     - go mod tidy
 builds:
-  - binary: build/usr/bin/casaos-user-service
+  - id: casaos-user-service-amd64
+    binary: build/usr/bin/casaos-user-service
     env:
-      - CGO_ENABLED=0
+      - CGO_ENABLED=1
+      - CC=x86_64-linux-gnu-gcc-10
     goos:
       - linux
     goarch:
       - amd64
+  - id: casaos-user-service-arm64
+    binary: build/usr/bin/casaos-user-service
+    env:
+      - CGO_ENABLED=1
+      - CC=aarch64-linux-gnu-gcc-10
+    goos:
+      - linux
+    goarch:
       - arm64
+  - id: casaos-user-service-arm-7
+    binary: build/usr/bin/casaos-user-service
+    env:
+      - CGO_ENABLED=1
+      - CC=arm-linux-gnueabihf-gcc-10
+    goos:
+      - linux
+    goarch:
+      - arm
+    goarm:
+      - 7
+  - id: casaos-user-service-migration-tool-amd64
+    binary: build/usr/bin/casaos-user-service-migration-tool
+    main: ./cmd/migration-tool
+    env:
+      - CGO_ENABLED=1
+      - CC=x86_64-linux-gnu-gcc-10
+    goos:
+      - linux
+    goarch:
+      - amd64
+  - id: casaos-user-service-migration-tool-arm64
+    binary: build/usr/bin/casaos-user-service-migration-tool
+    main: ./cmd/migration-tool
+    env:
+      - CGO_ENABLED=1
+      - CC=aarch64-linux-gnu-gcc-10
+    goos:
+      - linux
+    goarch:
+      - arm64
+  - id: casaos-user-service-migration-tool-arm-7
+    binary: build/usr/bin/casaos-user-service-migration-tool
+    main: ./cmd/migration-tool
+    env:
+      - CGO_ENABLED=1
+      - CC=arm-linux-gnueabihf-gcc-10
+    goos:
+      - linux
+    goarch:
       - arm
     goarm:
       - 7
 archives:
-  - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}"
+  - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-v{{ .Version }}"
+    id: casaos-user-service
+    builds:
+      - casaos-user-service-amd64
+      - casaos-user-service-arm64
+      - casaos-user-service-arm-7
+    replacements:
+      arm: arm-7
+    files:
+      - build/**/*
+  - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-migration-tool-v{{ .Version }}"
+    id: casaos-user-service-migration-tool
+    builds:
+      - casaos-user-service-migration-tool-amd64
+      - casaos-user-service-migration-tool-arm64
+      - casaos-user-service-migration-tool-arm-7
     replacements:
       arm: arm-7
     files:
diff --git a/build/etc/casaos/user-service.conf b/build/etc/casaos/user-service.conf.sample
similarity index 100%
rename from build/etc/casaos/user-service.conf
rename to build/etc/casaos/user-service.conf.sample
diff --git a/build/usr/share/casaos/migration/script.d/01-migrate-user-service.sh b/build/usr/share/casaos/migration/script.d/01-migrate-user-service.sh
new file mode 100644
index 0000000..0f61faa
--- /dev/null
+++ b/build/usr/share/casaos/migration/script.d/01-migrate-user-service.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+__get_existing_version() {
+    local version
+    
+    echo "$version"
+}
\ No newline at end of file
diff --git a/build/usr/share/casaos/migration/service.d/user-service/from-0.2.sh b/build/usr/share/casaos/migration/service.d/user-service/from-0.2.sh
new file mode 100644
index 0000000..e69de29
diff --git a/build/usr/share/casaos/migration/service.d/user-service/from-0.3.4.sh b/build/usr/share/casaos/migration/service.d/user-service/from-0.3.4.sh
new file mode 120000
index 0000000..2c3a78b
--- /dev/null
+++ b/build/usr/share/casaos/migration/service.d/user-service/from-0.3.4.sh
@@ -0,0 +1 @@
+from-0.3.sh
\ No newline at end of file
diff --git a/build/usr/share/casaos/migration/service.d/user-service/from-0.3.sh b/build/usr/share/casaos/migration/service.d/user-service/from-0.3.sh
new file mode 100644
index 0000000..e69de29
diff --git a/build/usr/share/casaos/setup/script.d/01-setup-user-service.sh b/build/usr/share/casaos/setup/script.d/01-setup-user-service.sh
new file mode 100644
index 0000000..94ef7de
--- /dev/null
+++ b/build/usr/share/casaos/setup/script.d/01-setup-user-service.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+set -e
+
diff --git a/build/usr/share/casaos/setup/service.d/user-service/debian/bullseye/setup-user-service.sh b/build/usr/share/casaos/setup/service.d/user-service/debian/bullseye/setup-user-service.sh
new file mode 120000
index 0000000..d1f69f2
--- /dev/null
+++ b/build/usr/share/casaos/setup/service.d/user-service/debian/bullseye/setup-user-service.sh
@@ -0,0 +1 @@
+../setup-user-service.sh
\ No newline at end of file
diff --git a/build/usr/share/casaos/setup/service.d/user-service/debian/setup-user-service.sh b/build/usr/share/casaos/setup/service.d/user-service/debian/setup-user-service.sh
new file mode 100644
index 0000000..e69de29
diff --git a/build/usr/share/casaos/setup/service.d/user-service/ubuntu/jammy/setup-user-service.sh b/build/usr/share/casaos/setup/service.d/user-service/ubuntu/jammy/setup-user-service.sh
new file mode 120000
index 0000000..d1f69f2
--- /dev/null
+++ b/build/usr/share/casaos/setup/service.d/user-service/ubuntu/jammy/setup-user-service.sh
@@ -0,0 +1 @@
+../setup-user-service.sh
\ No newline at end of file
diff --git a/build/usr/share/casaos/setup/service.d/user-service/ubuntu/setup-user-service.sh b/build/usr/share/casaos/setup/service.d/user-service/ubuntu/setup-user-service.sh
new file mode 120000
index 0000000..79d764b
--- /dev/null
+++ b/build/usr/share/casaos/setup/service.d/user-service/ubuntu/setup-user-service.sh
@@ -0,0 +1 @@
+../debian/setup-user-service.sh
\ No newline at end of file
diff --git a/cmd/migration-tool/log.go b/cmd/migration-tool/log.go
new file mode 100644
index 0000000..ed69983
--- /dev/null
+++ b/cmd/migration-tool/log.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+	"log"
+	"os"
+)
+
+type Logger struct {
+	DebugMode bool
+
+	_debug *log.Logger
+	_info  *log.Logger
+	_error *log.Logger
+}
+
+func NewLogger() *Logger {
+	return &Logger{
+		DebugMode: false,
+		_debug:    log.New(os.Stdout, "DEBUG: ", 0),
+		_info:     log.New(os.Stdout, "", 0),
+		_error:    log.New(os.Stderr, "ERROR: ", 0),
+	}
+}
+
+func (l *Logger) Debug(format string, v ...interface{}) {
+	if l.DebugMode {
+		l._debug.Printf(format, v...)
+	}
+}
+
+func (l *Logger) Info(format string, v ...interface{}) {
+	l._info.Printf(format, v...)
+}
+
+func (l *Logger) Error(format string, v ...interface{}) {
+	l._error.Printf(format, v...)
+}
diff --git a/cmd/migration-tool/main.go b/cmd/migration-tool/main.go
new file mode 100644
index 0000000..62bcb30
--- /dev/null
+++ b/cmd/migration-tool/main.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	interfaces "github.com/IceWhaleTech/CasaOS-Common"
+	"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
+	"github.com/IceWhaleTech/CasaOS-UserService/common"
+)
+
+const (
+	userServiceConfigSampleFilePath = "/etc/casaos/user-service.conf.sample"
+	userServiceName                 = "casaos-user-service.service"
+)
+
+var _logger *Logger
+
+func main() {
+	versionFlag := flag.Bool("v", false, "version")
+	debugFlag := flag.Bool("d", true, "debug")
+	forceFlag := flag.Bool("f", false, "force")
+	flag.Parse()
+
+	if *versionFlag {
+		fmt.Println(common.Version)
+		os.Exit(0)
+	}
+
+	_logger = NewLogger()
+
+	if os.Getuid() != 0 {
+		_logger.Info("Root privileges are required to run this program.")
+		os.Exit(1)
+	}
+
+	if *debugFlag {
+		_logger.DebugMode = true
+	}
+
+	if !*forceFlag {
+		serviceEnabled, err := systemctl.IsServiceEnabled(userServiceName)
+		if err != nil {
+			_logger.Error("Failed to check if %s is enabled", userServiceName)
+			panic(err)
+		}
+
+		if serviceEnabled {
+			_logger.Info("%s is already enabled. If migration is still needed, try with -f.", userServiceName)
+			os.Exit(1)
+		}
+	}
+
+	migrationTools := []interfaces.MigrationTool{
+		NewMigrationToolFor033_034_035(),
+	}
+
+	var selectedMigrationTool interfaces.MigrationTool
+
+	// look for the right migration tool matching current version
+	for _, tool := range migrationTools {
+		migrationNeeded, err := tool.IsMigrationNeeded()
+		if err != nil {
+			panic(err)
+		}
+
+		if migrationNeeded {
+			selectedMigrationTool = tool
+			break
+		}
+	}
+
+	if selectedMigrationTool == nil {
+		_logger.Info("No migration to proceed.")
+		return
+	}
+
+	if err := selectedMigrationTool.PreMigrate(); err != nil {
+		panic(err)
+	}
+
+	if err := selectedMigrationTool.Migrate(); err != nil {
+		panic(err)
+	}
+
+	if err := selectedMigrationTool.PostMigrate(); err != nil {
+		panic(err)
+	}
+}
diff --git a/cmd/migration-tool/migration-033-034-035.go b/cmd/migration-tool/migration-033-034-035.go
new file mode 100644
index 0000000..bcec2a5
--- /dev/null
+++ b/cmd/migration-tool/migration-033-034-035.go
@@ -0,0 +1,97 @@
+package main
+
+import (
+	interfaces "github.com/IceWhaleTech/CasaOS-Common"
+	"github.com/IceWhaleTech/CasaOS-Common/utils/version"
+	"github.com/IceWhaleTech/CasaOS-UserService/pkg/config"
+	"github.com/IceWhaleTech/CasaOS-UserService/pkg/utils/file"
+	"gopkg.in/ini.v1"
+)
+
+type migrationTool struct{}
+
+func (u *migrationTool) IsMigrationNeeded() (bool, error) {
+	_logger.Info("Checking if migration is needed for CasaoS version between 0.3.3 and 0.3.5...")
+
+	minorVersion, err := version.DetectMinorVersion()
+	if err != nil {
+		return false, err
+	}
+
+	if minorVersion != 3 {
+		return false, nil
+	}
+
+	// this is the best way to tell if CasaOS version is between 0.3.3 and 0.3.5
+	isUserDataInDatabase, err := version.IsUserDataInDatabase()
+	if err != nil {
+		return false, err
+	}
+
+	if !isUserDataInDatabase {
+		return false, nil
+	}
+
+	return true, nil
+}
+
+func (u *migrationTool) PreMigrate() error {
+	_logger.Info("Copying %s to %s if it doesn't exist...", userServiceConfigSampleFilePath, config.UserServiceConfigFilePath)
+	if err := file.CopySingleFile(userServiceConfigSampleFilePath, config.UserServiceConfigFilePath, "skip"); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (u *migrationTool) Migrate() error {
+	_logger.Info("Loading legacy %s...", version.LegacyCasaOSConfigFilePath)
+	legacyConfigFile, err := ini.Load(version.LegacyCasaOSConfigFilePath)
+	if err != nil {
+		return err
+	}
+
+	// LogPath
+	logPath, err := legacyConfigFile.Section("app").GetKey("LogPath")
+	if err != nil {
+		return err
+	}
+
+	// LogFileExt
+	logFileExt, err := legacyConfigFile.Section("app").GetKey("LogFileExt")
+	if err != nil {
+		return err
+	}
+
+	// DBPath
+	dbPath, err := legacyConfigFile.Section("app").GetKey("DBPath")
+	if err != nil {
+		return err
+	}
+
+	// UserDataPath
+	userDataPath, err := legacyConfigFile.Section("app").GetKey("UserDataPath")
+	if err != nil {
+		return err
+	}
+
+	_logger.Info("Updating %s with settings from legacy configuration...", config.UserServiceConfigFilePath)
+	config.InitSetup(config.UserServiceConfigFilePath)
+
+	config.AppInfo.LogPath = logPath.Value()
+	config.AppInfo.LogSaveName = logSaveName.Value()
+	config.AppInfo.LogFileExt = logFileExt.Value()
+	config.AppInfo.DBPath = dbPath.Value()
+	config.AppInfo.UserDataPath = userDataPath.Value()
+
+	config.SaveSetup(config.UserServiceConfigFilePath)
+
+	return nil
+}
+
+func (u *migrationTool) PostMigrate() error {
+	return nil
+}
+
+func NewMigrationToolFor033_034_035() interfaces.MigrationTool {
+	return &migrationTool{}
+}
diff --git a/common/version.go b/common/version.go
new file mode 100644
index 0000000..f3f9408
--- /dev/null
+++ b/common/version.go
@@ -0,0 +1,3 @@
+package common
+
+const Version = "0.3.5"
diff --git a/go.mod b/go.mod
index d60fed9..291de40 100644
--- a/go.mod
+++ b/go.mod
@@ -3,25 +3,27 @@ module github.com/IceWhaleTech/CasaOS-UserService
 go 1.18
 
 require (
-	github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220804224534-081d69c201cc
+	github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220811230555-229797f76426
 	github.com/IceWhaleTech/CasaOS-Gateway v0.0.0-20220804231126-285796241a3b
 	github.com/gin-contrib/gzip v0.0.6
 	github.com/gin-gonic/gin v1.8.1
 	github.com/satori/go.uuid v1.2.0
 	github.com/tidwall/gjson v1.14.1
 	go.uber.org/zap v1.21.0
-	gopkg.in/ini.v1 v1.66.6
+	gopkg.in/ini.v1 v1.67.0
 	gorm.io/driver/sqlite v1.3.6
 	gorm.io/gorm v1.23.8
 )
 
 require (
 	github.com/benbjohnson/clock v1.3.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-playground/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.0 // indirect
 	github.com/go-playground/validator/v10 v10.11.0 // indirect
 	github.com/goccy/go-json v0.9.10 // indirect
+	github.com/godbus/dbus/v5 v5.0.4 // indirect
 	github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
diff --git a/go.sum b/go.sum
index 3cb538d..3701484 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,13 @@
 github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
-github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220804224534-081d69c201cc h1:PVQLOa4Ao9vmVDi8bontpDQct81oE4NQJ72XEuo/7PU=
-github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220804224534-081d69c201cc/go.mod h1:2WIgP2lnFQ/1TfCnXW00JJIKm3coLyV3WqAaTCAYY28=
+github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220811230555-229797f76426 h1:+Yc9qJ6AehudNsIRkoyTe8dmHiHDWsPF/QL7qk8Va6o=
+github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220811230555-229797f76426/go.mod h1:XGqdbedN9UlF3/rylcXKJ2BW4ayugBmEMa4Z0tk2KbQ=
 github.com/IceWhaleTech/CasaOS-Gateway v0.0.0-20220804231126-285796241a3b h1:IiMCqvGelQLGTX151gqVwrzoPQVJy8Q2JAvkhjiQ6tY=
 github.com/IceWhaleTech/CasaOS-Gateway v0.0.0-20220804231126-285796241a3b/go.mod h1:jcURlZtPPQJJvfIW4ZgDTtpFfak7bPTvKZUxWxf62M8=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
 github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -28,6 +30,8 @@ github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4
 github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
 github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
 github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -154,8 +158,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
-gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ed32f4b..70fbbe2 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,15 +1,5 @@
-/*
- * @Author: LinkLeong link@icewhale.com
- * @Date: 2021-09-30 18:18:14
- * @LastEditors: LinkLeong
- * @LastEditTime: 2022-06-21 11:09:30
- * @FilePath: /CasaOS/pkg/config/config.go
- * @Description:
- * @Website: https://www.casaos.io
- * Copyright (c) 2022 by icewhale, All Rights Reserved.
- */
 package config
 
 const (
-	USERCONFIGURL = "/etc/casaos/casaos.conf"
+	UserServiceConfigFilePath = "/etc/casaos/user-service.conf"
 )
diff --git a/pkg/config/init.go b/pkg/config/init.go
index 4045b32..38d9f56 100644
--- a/pkg/config/init.go
+++ b/pkg/config/init.go
@@ -26,15 +26,14 @@ var AppInfo = &model.APPModel{
 var Cfg *ini.File
 
 func InitSetup(config string) {
-
-	var configDir = USERCONFIGURL
+	configFilePath := UserServiceConfigFilePath
 	if len(config) > 0 {
-		configDir = config
+		configFilePath = config
 	}
 
 	var err error
 
-	Cfg, err = ini.Load(configDir)
+	Cfg, err = ini.Load(configFilePath)
 	if err != nil {
 		fmt.Printf("Fail to read file: %v", err)
 		os.Exit(1)
@@ -44,9 +43,31 @@ func InitSetup(config string) {
 	mapTo("app", AppInfo)
 }
 
+func SaveSetup(config string) {
+	reflectFrom("common", CommonInfo)
+	reflectFrom("app", AppInfo)
+
+	configFilePath := UserServiceConfigFilePath
+	if len(config) > 0 {
+		configFilePath = config
+	}
+
+	if err := Cfg.SaveTo(configFilePath); err != nil {
+		fmt.Printf("Fail to save file: %v", err)
+		os.Exit(1)
+	}
+}
+
 func mapTo(section string, v interface{}) {
 	err := Cfg.Section(section).MapTo(v)
 	if err != nil {
 		log.Fatalf("Cfg.MapTo %s err: %v", section, err)
 	}
 }
+
+func reflectFrom(section string, v interface{}) {
+	err := Cfg.Section(section).ReflectFrom(v)
+	if err != nil {
+		log.Fatalf("Cfg.ReflectFrom %s err: %v", section, err)
+	}
+}