| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package install |
| |
| import ( |
| "encoding/json" |
| "net/http" |
| "os" |
| "path/filepath" |
| "time" |
| |
| "github.com/apache/answer/configs" |
| "github.com/apache/answer/internal/base/conf" |
| "github.com/apache/answer/internal/base/data" |
| "github.com/apache/answer/internal/base/handler" |
| "github.com/apache/answer/internal/base/path" |
| "github.com/apache/answer/internal/base/reason" |
| "github.com/apache/answer/internal/base/translator" |
| "github.com/apache/answer/internal/cli" |
| "github.com/apache/answer/internal/migrations" |
| "github.com/apache/answer/internal/schema" |
| "github.com/gin-gonic/gin" |
| "github.com/jinzhu/copier" |
| "github.com/segmentfault/pacman/errors" |
| "github.com/segmentfault/pacman/i18n" |
| "github.com/segmentfault/pacman/log" |
| ) |
| |
| // LangOptions get installation language options |
| // @Summary get installation language options |
| // @Description get installation language options |
| // @Tags Lang |
| // @Produce json |
| // @Success 200 {object} handler.RespBody{data=[]translator.LangOption} |
| // @Router /installation/language/options [get] |
| func LangOptions(ctx *gin.Context) { |
| handler.HandleResponse(ctx, nil, translator.LanguageOptions) |
| } |
| |
| // GetLangMapping get installation language config mapping |
| // @Summary get installation language config mapping |
| // @Description get installation language config mapping |
| // @Tags Lang |
| // @Param lang query string true "installation language" |
| // @Produce json |
| // @Success 200 {object} handler.RespBody{} |
| // @Router /installation/language/config [get] |
| func GetLangMapping(ctx *gin.Context) { |
| t, err := translator.NewTranslator(&translator.I18n{BundleDir: path.I18nPath}) |
| if err != nil { |
| handler.HandleResponse(ctx, err, nil) |
| return |
| } |
| lang := ctx.Query("lang") |
| trData, _ := t.Dump(i18n.Language(lang)) |
| var resp map[string]any |
| _ = json.Unmarshal(trData, &resp) |
| handler.HandleResponse(ctx, nil, resp) |
| } |
| |
| // CheckConfigFileAndRedirectToInstallPage if config file not exist try to redirect to install page |
| // @Summary if config file not exist try to redirect to install page |
| // @Description if config file not exist try to redirect to install page |
| // @Tags installation |
| // @Accept json |
| // @Produce json |
| // @Router / [get] |
| func CheckConfigFileAndRedirectToInstallPage(ctx *gin.Context) { |
| if cli.CheckConfigFile(confPath) { |
| ctx.Redirect(http.StatusFound, "/50x") |
| } else { |
| ctx.Redirect(http.StatusFound, "/install") |
| } |
| } |
| |
| // CheckConfigFile check config file if exist when installation |
| // @Summary check config file if exist when installation |
| // @Description check config file if exist when installation |
| // @Tags installation |
| // @Accept json |
| // @Produce json |
| // @Success 200 {object} handler.RespBody{data=install.CheckConfigFileResp{}} |
| // @Router /installation/config-file/check [post] |
| func CheckConfigFile(ctx *gin.Context) { |
| resp := &CheckConfigFileResp{} |
| resp.ConfigFileExist = cli.CheckConfigFile(confPath) |
| if !resp.ConfigFileExist { |
| handler.HandleResponse(ctx, nil, resp) |
| return |
| } |
| allConfig, err := conf.ReadConfig(confPath) |
| if err != nil { |
| log.Error(err) |
| err = errors.BadRequest(reason.ReadConfigFailed) |
| handler.HandleResponse(ctx, err, nil) |
| return |
| } |
| resp.DBConnectionSuccess = cli.CheckDBConnection(allConfig.Data.Database) |
| if resp.DBConnectionSuccess { |
| resp.DbTableExist = cli.CheckDBTableExist(allConfig.Data.Database) |
| } |
| handler.HandleResponse(ctx, nil, resp) |
| } |
| |
| // CheckDatabase check database if exist when installation |
| // @Summary check database if exist when installation |
| // @Description check database if exist when installation |
| // @Tags installation |
| // @Accept json |
| // @Produce json |
| // @Param data body install.CheckDatabaseReq true "CheckDatabaseReq" |
| // @Success 200 {object} handler.RespBody{data=install.CheckConfigFileResp{}} |
| // @Router /installation/db/check [post] |
| func CheckDatabase(ctx *gin.Context) { |
| req := &CheckDatabaseReq{} |
| if handler.BindAndCheck(ctx, req) { |
| return |
| } |
| |
| resp := &CheckDatabaseResp{} |
| dataConf := &data.Database{ |
| Driver: req.DbType, |
| Connection: req.GetConnection(), |
| } |
| resp.ConnectionSuccess = cli.CheckDBConnection(dataConf) |
| if !resp.ConnectionSuccess { |
| handler.HandleResponse(ctx, errors.BadRequest(reason.DatabaseConnectionFailed), schema.ErrTypeAlert) |
| return |
| } |
| handler.HandleResponse(ctx, nil, resp) |
| } |
| |
| // InitEnvironment init environment |
| // @Summary init environment |
| // @Description init environment |
| // @Tags installation |
| // @Accept json |
| // @Produce json |
| // @Param data body install.CheckDatabaseReq true "CheckDatabaseReq" |
| // @Success 200 {object} handler.RespBody{} |
| // @Router /installation/init [post] |
| func InitEnvironment(ctx *gin.Context) { |
| req := &CheckDatabaseReq{} |
| if handler.BindAndCheck(ctx, req) { |
| return |
| } |
| |
| // check config file if exist |
| if cli.CheckConfigFile(confPath) { |
| log.Debug("config file already exists") |
| handler.HandleResponse(ctx, nil, nil) |
| return |
| } |
| |
| if err := cli.InstallConfigFile(confPath); err != nil { |
| handler.HandleResponse(ctx, errors.BadRequest(reason.InstallConfigFailed), &InitEnvironmentResp{ |
| Success: false, |
| CreateConfigFailed: true, |
| DefaultConfig: string(configs.Config), |
| ErrType: schema.ErrTypeAlert.ErrType, |
| }) |
| return |
| } |
| |
| c, err := conf.ReadConfig(confPath) |
| if err != nil { |
| log.Errorf("read config failed %s", err) |
| handler.HandleResponse(ctx, errors.BadRequest(reason.ReadConfigFailed), nil) |
| return |
| } |
| c.Data.Database.Driver = req.DbType |
| c.Data.Database.Connection = req.GetConnection() |
| c.Data.Cache.FilePath = filepath.Join(path.CacheDir, path.DefaultCacheFileName) |
| c.I18n.BundleDir = path.I18nPath |
| c.ServiceConfig.UploadPath = path.UploadFilePath |
| |
| if err := conf.RewriteConfig(confPath, c); err != nil { |
| log.Errorf("rewrite config failed %s", err) |
| handler.HandleResponse(ctx, errors.BadRequest(reason.ReadConfigFailed), nil) |
| return |
| } |
| handler.HandleResponse(ctx, nil, nil) |
| } |
| |
| // InitBaseInfo init base info |
| // @Summary init base info |
| // @Description init base info |
| // @Tags installation |
| // @Accept json |
| // @Produce json |
| // @Param data body install.InitBaseInfoReq true "InitBaseInfoReq" |
| // @Success 200 {object} handler.RespBody{} |
| // @Router /installation/base-info [post] |
| func InitBaseInfo(ctx *gin.Context) { |
| req := &InitBaseInfoReq{} |
| if handler.BindAndCheck(ctx, req) { |
| return |
| } |
| req.FormatSiteUrl() |
| |
| c, err := conf.ReadConfig(confPath) |
| if err != nil { |
| log.Errorf("read config failed %s", err) |
| handler.HandleResponse(ctx, errors.BadRequest(reason.ReadConfigFailed), nil) |
| return |
| } |
| |
| if cli.CheckDBTableExist(c.Data.Database) { |
| log.Warn("database is already initialized") |
| handler.HandleResponse(ctx, nil, nil) |
| return |
| } |
| |
| engine, err := data.NewDB(false, c.Data.Database) |
| if err != nil { |
| log.Errorf("init database failed %s", err) |
| handler.HandleResponse(ctx, errors.BadRequest(reason.InstallCreateTableFailed), nil) |
| } |
| |
| inputData := &migrations.InitNeedUserInputData{} |
| _ = copier.Copy(inputData, req) |
| if err := migrations.NewMentor(ctx, engine, inputData).InitDB(); err != nil { |
| log.Error("init database error: ", err.Error()) |
| handler.HandleResponse(ctx, errors.BadRequest(reason.InstallConfigFailed), schema.ErrTypeAlert) |
| return |
| } |
| |
| handler.HandleResponse(ctx, nil, nil) |
| go func() { |
| time.Sleep(1 * time.Second) |
| os.Exit(0) |
| }() |
| } |