// 检测 cfg 的各项字段是否合法, // 初始化相关内容。比如静态文件路由等。 func (cfg *Config) init() error { if len(cfg.Port) == 0 { if cfg.HTTPS { cfg.Port = httpsPort } else { cfg.Port = httpPort } } if cfg.Port[0] != ':' { cfg.Port = ":" + cfg.Port } if cfg.HTTPState < 0 || cfg.HTTPState >= httpStateSize { return errors.New("无效的httpState值") } if cfg.HTTPS { if !utils.FileExists(cfg.CertFile) { return errors.New("certFile 所指的文件并不存在") } if !utils.FileExists(cfg.KeyFile) { return errors.New("keyFile 所指的文件并不存在") } } return nil }
// 用于输出配置文件到指定的位置。 // 目前包含了日志配置文件和程序本身的配置文件。 func installConfig() error { if !utils.FileExists(Appdir(configDir)) { if err := os.MkdirAll(Appdir(configDir), os.ModePerm); err != nil { return err } if !utils.FileExists(Appdir(configDir)) { return fmt.Errorf("路径[%v]不存在,且无法创建", Appdir(configDir)) } } if err := ioutil.WriteFile(Appdir(logConfigFile), static.LogConfig, os.ModePerm); err != nil { return err } cfg := &Config{ Core: &web.Config{ HTTPS: false, CertFile: "", KeyFile: "", Port: "8080", Headers: map[string]string{ "Server": "typing", }, }, Debug: true, AdminURLPrefix: "/admin", Salt: rands.String(6, 7, rands.Lower, rands.Upper, rands.Digit, rands.Punct), DBDSN: "./output/main.db", DBPrefix: "typing_", DBDriver: "sqlite3", FrontAPIPrefix: "/api", AdminAPIPrefix: "/admin/api", ThemeURLPrefix: "/themes", UploadDirFormat: "2006/01/", UploadExts: ".txt;.png;.jpg;.jpeg", UploadSize: 1024 * 1024 * 5, UploadURLPrefix: "/uploads", } data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } return ioutil.WriteFile(Appdir(configFile), data, os.ModePerm) }
// Init 对 Options 作一些初始化操作。 func (o *Options) Init() *app.OptionsError { if len(o.Dir) == 0 { return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)} } if len(o.Type) == 0 { return &app.OptionsError{Field: "type", Message: locale.Sprintf(locale.ErrRequired)} } if !utils.FileExists(o.Dir) { if err := os.MkdirAll(o.Dir, os.ModePerm); err != nil { msg := locale.Sprintf(locale.ErrMkdirError, err) return &app.OptionsError{Field: "dir", Message: msg} } } if !isSuppertedType(o.Type) { return &app.OptionsError{Field: "type", Message: locale.Sprintf(locale.ErrInvalidFormat)} } // 只有 html 和 html+ 才需要判断模板文件是否存在 if o.Type == "html" || o.Type == "html+" { if len(o.Template) > 0 && !utils.FileExists(o.Template) { msg := locale.Sprintf(locale.ErrTemplateNotExists) return &app.OptionsError{Field: "template", Message: msg} } } // 调试模式,必须得有模板和端口 if o.Type == "html+" { if len(o.Template) == 0 { return &app.OptionsError{Field: "template", Message: locale.Sprintf(locale.ErrRequired)} } if len(o.Port) == 0 { return &app.OptionsError{Field: "port", Message: locale.Sprintf(locale.ErrRequired)} } if o.Port[0] != ':' { o.Port = ":" + o.Port } } return nil }
// Init 对 Options 作一些初始化操作。 func (o *Options) Init() *app.OptionsError { if len(o.Dir) == 0 { return &app.OptionsError{Field: "dir", Message: "不能为空"} } if len(o.Type) == 0 { return &app.OptionsError{Field: "type", Message: "不能为空"} } if !utils.FileExists(o.Dir) { if err := os.MkdirAll(o.Dir, os.ModePerm); err != nil { msg := "不存在,且在创建时提示以下信息:" + err.Error() return &app.OptionsError{Field: "dir", Message: msg} } } if !isSuppertedType(o.Type) { return &app.OptionsError{Field: "type", Message: "不支持的类型"} } // 只有 html 和 html+ 才需要判断模板文件是否存在 if o.Type == "html" || o.Type == "html+" { if len(o.Template) > 0 && !utils.FileExists(o.Template) { return &app.OptionsError{Field: "template", Message: "模板目录不存在"} } } // 调试模式,必须得有模板和端口 if o.Type == "html+" { if len(o.Template) == 0 { return &app.OptionsError{Field: "template", Message: "不能为空"} } if len(o.Port) == 0 { return &app.OptionsError{Field: "port", Message: "不能为空"} } if o.Port[0] != ':' { o.Port = ":" + o.Port } } return nil }
// 首页 func pageRoot(w http.ResponseWriter, r *http.Request) { // 首页的匹配模式为:/,可以匹配任意路径。所以此处作个判断,只有完全匹配的,才是显示首页 if r.URL.Path == app.HomeURL() { pagePosts(w, r) return } path := cfg.RootDir + r.URL.Path if utils.FileExists(path) { http.ServeFile(w, r, path) return } pageHttpStatusCode(w, r, http.StatusNotFound) }
// 输出一个特定状态码下的错误页面。若该页面模板不存在,则只输出状态码,而没有内容。 // 只对状态码大于等于400的起作用。 func pageHttpStatusCode(w http.ResponseWriter, r *http.Request, code int) { if code < 400 { return } w.Header().Set("Content-Type", "text/html") w.WriteHeader(code) path := themeDir(currentTheme) + strconv.Itoa(code) + ".html" if !utils.FileExists(path) { // 文件不存在,则只输出状态码,省略内容。 return } // TODO: serveFile会自动写入一个状态码,导致多次输出状态码的提示 http.ServeFile(w, r, path) }
// 初始化app包。 // 除Install函数,其它函数都依赖Init()做初始化。 // // dir 用于指定当前程序的数据存放路径。 func Init(dir string) (err error) { if !utils.FileExists(dir) { return fmt.Errorf("appdir[%v]不存在", appdir) } if !strings.HasSuffix(dir, "/") && !strings.HasSuffix(dir, string(os.PathSeparator)) { dir += string(os.PathSeparator) } appdir = dir // 初始化日志系统 if err := logs.InitFromXMLFile(Appdir(logConfigFile)); err != nil { return err } // 加载app.json配置文件 config, err = loadConfig(Appdir(configFile)) if err != nil { return err } // 根据配置文件初始化数据库 db, err = initDB() if err != nil { return err } // 加载数据库中的配置项 options, err = loadOptions() if err != nil { return err } // 初始化系统的状态数据。 stats, err = loadStats() if err != nil { return err } return nil }
// Init 检测 Options 变量是否符合要求 func (opt *Options) Init() *app.OptionsError { if len(opt.Dir) == 0 { return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)} } if !utils.FileExists(opt.Dir) { return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrDirNotExists)} } if len(opt.Lang) == 0 { return &app.OptionsError{Field: "lang", Message: locale.Sprintf(locale.ErrRequired)} } if !langIsSupported(opt.Lang) { return &app.OptionsError{Field: "lang", Message: locale.Sprintf(locale.ErrUnsupportedInputLang, opt.Lang)} } if len(opt.Exts) > 0 { exts := make([]string, 0, len(opt.Exts)) for _, ext := range opt.Exts { if len(ext) == 0 { continue } if ext[0] != '.' { ext = "." + ext } exts = append(exts, ext) } opt.Exts = exts } else { opt.Exts = langExts[opt.Lang] } return nil }
// Init 检测 Options 变量是否符合要求 func (opt *Options) Init() *app.OptionsError { if len(opt.Dir) == 0 { return &app.OptionsError{Field: "dir", Message: "不能为空"} } if !utils.FileExists(opt.Dir) { return &app.OptionsError{Field: "dir", Message: "该目录不存在"} } if len(opt.Lang) == 0 { return &app.OptionsError{Field: "lang", Message: "不能为空"} } if !langIsSupported(opt.Lang) { return &app.OptionsError{Field: "lang", Message: "不支持该语言"} } if len(opt.Exts) > 0 { exts := make([]string, 0, len(opt.Exts)) for _, ext := range opt.Exts { if len(ext) == 0 { continue } if ext[0] != '.' { ext = "." + ext } exts = append(exts, ext) } opt.Exts = exts } else { opt.Exts = langExts[opt.Lang] } return nil }