A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 

353 rindas
8.3 KiB

  1. package config
  2. import (
  3. "fmt"
  4. "github.com/fatih/color"
  5. "github.com/manifoldco/promptui"
  6. "github.com/mitchellh/go-wordwrap"
  7. "github.com/writeas/web-core/auth"
  8. "strconv"
  9. )
  10. type SetupData struct {
  11. User *UserCreation
  12. Config *Config
  13. }
  14. func Configure(fname string) (*SetupData, error) {
  15. data := &SetupData{}
  16. var err error
  17. if fname == "" {
  18. fname = FileName
  19. }
  20. data.Config, err = Load(fname)
  21. var action string
  22. isNewCfg := false
  23. if err != nil {
  24. fmt.Printf("No %s configuration yet. Creating new.\n", fname)
  25. data.Config = New()
  26. action = "generate"
  27. isNewCfg = true
  28. } else {
  29. fmt.Printf("Loaded configuration %s.\n", fname)
  30. action = "update"
  31. }
  32. title := color.New(color.Bold, color.BgGreen).PrintFunc()
  33. intro := color.New(color.Bold, color.FgWhite).PrintlnFunc()
  34. fmt.Println()
  35. intro(" ✍ Write Freely Configuration ✍")
  36. fmt.Println()
  37. fmt.Println(wordwrap.WrapString(" This quick configuration process will "+action+" the application's config file, "+fname+".\n\n It validates your input along the way, so you can be sure any future errors aren't caused by a bad configuration. If you'd rather configure your server manually, instead run: writefreely --create-config and edit that file.", 75))
  38. fmt.Println()
  39. title(" Server setup ")
  40. fmt.Println()
  41. tmpls := &promptui.PromptTemplates{
  42. Success: "{{ . | bold | faint }}: ",
  43. }
  44. selTmpls := &promptui.SelectTemplates{
  45. Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`),
  46. }
  47. // Environment selection
  48. selPrompt := promptui.Select{
  49. Templates: selTmpls,
  50. Label: "Environment",
  51. Items: []string{"Development", "Production, standalone", "Production, behind reverse proxy"},
  52. }
  53. _, envType, err := selPrompt.Run()
  54. if err != nil {
  55. return data, err
  56. }
  57. isDevEnv := envType == "Development"
  58. isStandalone := envType == "Production, standalone"
  59. data.Config.Server.Dev = isDevEnv
  60. var prompt promptui.Prompt
  61. if isDevEnv || !isStandalone {
  62. // Running in dev environment or behind reverse proxy; ask for port
  63. prompt = promptui.Prompt{
  64. Templates: tmpls,
  65. Label: "Local port",
  66. Validate: validatePort,
  67. Default: fmt.Sprintf("%d", data.Config.Server.Port),
  68. }
  69. port, err := prompt.Run()
  70. if err != nil {
  71. return data, err
  72. }
  73. data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number
  74. }
  75. if isStandalone {
  76. selPrompt = promptui.Select{
  77. Templates: selTmpls,
  78. Label: "Web server mode",
  79. Items: []string{"Insecure (port 80)", "Secure (port 443)"},
  80. }
  81. sel, _, err := selPrompt.Run()
  82. if err != nil {
  83. return data, err
  84. }
  85. if sel == 0 {
  86. data.Config.Server.Port = 80
  87. data.Config.Server.TLSCertPath = ""
  88. data.Config.Server.TLSKeyPath = ""
  89. } else if sel == 1 {
  90. data.Config.Server.Port = 443
  91. prompt = promptui.Prompt{
  92. Templates: tmpls,
  93. Label: "Certificate path",
  94. Validate: validateNonEmpty,
  95. Default: data.Config.Server.TLSCertPath,
  96. }
  97. data.Config.Server.TLSCertPath, err = prompt.Run()
  98. if err != nil {
  99. return data, err
  100. }
  101. prompt = promptui.Prompt{
  102. Templates: tmpls,
  103. Label: "Key path",
  104. Validate: validateNonEmpty,
  105. Default: data.Config.Server.TLSKeyPath,
  106. }
  107. data.Config.Server.TLSKeyPath, err = prompt.Run()
  108. if err != nil {
  109. return data, err
  110. }
  111. }
  112. } else {
  113. data.Config.Server.TLSCertPath = ""
  114. data.Config.Server.TLSKeyPath = ""
  115. }
  116. fmt.Println()
  117. title(" Database setup ")
  118. fmt.Println()
  119. selPrompt = promptui.Select{
  120. Templates: selTmpls,
  121. Label: "Database driver",
  122. Items: []string{"MySQL", "SQLite"},
  123. }
  124. sel, _, err := selPrompt.Run()
  125. if err != nil {
  126. return data, err
  127. }
  128. if sel == 0 {
  129. // Configure for MySQL
  130. data.Config.UseMySQL(isNewCfg)
  131. prompt = promptui.Prompt{
  132. Templates: tmpls,
  133. Label: "Username",
  134. Validate: validateNonEmpty,
  135. Default: data.Config.Database.User,
  136. }
  137. data.Config.Database.User, err = prompt.Run()
  138. if err != nil {
  139. return data, err
  140. }
  141. prompt = promptui.Prompt{
  142. Templates: tmpls,
  143. Label: "Password",
  144. Validate: validateNonEmpty,
  145. Default: data.Config.Database.Password,
  146. Mask: '*',
  147. }
  148. data.Config.Database.Password, err = prompt.Run()
  149. if err != nil {
  150. return data, err
  151. }
  152. prompt = promptui.Prompt{
  153. Templates: tmpls,
  154. Label: "Database name",
  155. Validate: validateNonEmpty,
  156. Default: data.Config.Database.Database,
  157. }
  158. data.Config.Database.Database, err = prompt.Run()
  159. if err != nil {
  160. return data, err
  161. }
  162. prompt = promptui.Prompt{
  163. Templates: tmpls,
  164. Label: "Host",
  165. Validate: validateNonEmpty,
  166. Default: data.Config.Database.Host,
  167. }
  168. data.Config.Database.Host, err = prompt.Run()
  169. if err != nil {
  170. return data, err
  171. }
  172. prompt = promptui.Prompt{
  173. Templates: tmpls,
  174. Label: "Port",
  175. Validate: validatePort,
  176. Default: fmt.Sprintf("%d", data.Config.Database.Port),
  177. }
  178. dbPort, err := prompt.Run()
  179. if err != nil {
  180. return data, err
  181. }
  182. data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number
  183. } else if sel == 1 {
  184. // Configure for SQLite
  185. data.Config.UseSQLite(isNewCfg)
  186. prompt = promptui.Prompt{
  187. Templates: tmpls,
  188. Label: "Filename",
  189. Validate: validateNonEmpty,
  190. Default: data.Config.Database.FileName,
  191. }
  192. data.Config.Database.FileName, err = prompt.Run()
  193. if err != nil {
  194. return data, err
  195. }
  196. }
  197. fmt.Println()
  198. title(" App setup ")
  199. fmt.Println()
  200. selPrompt = promptui.Select{
  201. Templates: selTmpls,
  202. Label: "Site type",
  203. Items: []string{"Single user blog", "Multi-user instance"},
  204. }
  205. _, usersType, err := selPrompt.Run()
  206. if err != nil {
  207. return data, err
  208. }
  209. data.Config.App.SingleUser = usersType == "Single user blog"
  210. if data.Config.App.SingleUser {
  211. data.User = &UserCreation{}
  212. // prompt for username
  213. prompt = promptui.Prompt{
  214. Templates: tmpls,
  215. Label: "Admin username",
  216. Validate: validateNonEmpty,
  217. }
  218. data.User.Username, err = prompt.Run()
  219. if err != nil {
  220. return data, err
  221. }
  222. // prompt for password
  223. prompt = promptui.Prompt{
  224. Templates: tmpls,
  225. Label: "Admin password",
  226. Validate: validateNonEmpty,
  227. }
  228. newUserPass, err := prompt.Run()
  229. if err != nil {
  230. return data, err
  231. }
  232. data.User.HashedPass, err = auth.HashPass([]byte(newUserPass))
  233. if err != nil {
  234. return data, err
  235. }
  236. }
  237. siteNameLabel := "Instance name"
  238. if data.Config.App.SingleUser {
  239. siteNameLabel = "Blog name"
  240. }
  241. prompt = promptui.Prompt{
  242. Templates: tmpls,
  243. Label: siteNameLabel,
  244. Validate: validateNonEmpty,
  245. Default: data.Config.App.SiteName,
  246. }
  247. data.Config.App.SiteName, err = prompt.Run()
  248. if err != nil {
  249. return data, err
  250. }
  251. prompt = promptui.Prompt{
  252. Templates: tmpls,
  253. Label: "Public URL",
  254. Validate: validateDomain,
  255. Default: data.Config.App.Host,
  256. }
  257. data.Config.App.Host, err = prompt.Run()
  258. if err != nil {
  259. return data, err
  260. }
  261. if !data.Config.App.SingleUser {
  262. selPrompt = promptui.Select{
  263. Templates: selTmpls,
  264. Label: "Registration",
  265. Items: []string{"Open", "Closed"},
  266. }
  267. _, regType, err := selPrompt.Run()
  268. if err != nil {
  269. return data, err
  270. }
  271. data.Config.App.OpenRegistration = regType == "Open"
  272. prompt = promptui.Prompt{
  273. Templates: tmpls,
  274. Label: "Max blogs per user",
  275. Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs),
  276. }
  277. maxBlogs, err := prompt.Run()
  278. if err != nil {
  279. return data, err
  280. }
  281. data.Config.App.MaxBlogs, _ = strconv.Atoi(maxBlogs) // Ignore error, as we've already validated number
  282. }
  283. selPrompt = promptui.Select{
  284. Templates: selTmpls,
  285. Label: "Federation",
  286. Items: []string{"Enabled", "Disabled"},
  287. }
  288. _, fedType, err := selPrompt.Run()
  289. if err != nil {
  290. return data, err
  291. }
  292. data.Config.App.Federation = fedType == "Enabled"
  293. if data.Config.App.Federation {
  294. selPrompt = promptui.Select{
  295. Templates: selTmpls,
  296. Label: "Federation usage stats",
  297. Items: []string{"Public", "Private"},
  298. }
  299. _, fedStatsType, err := selPrompt.Run()
  300. if err != nil {
  301. return data, err
  302. }
  303. data.Config.App.PublicStats = fedStatsType == "Public"
  304. selPrompt = promptui.Select{
  305. Templates: selTmpls,
  306. Label: "Instance metadata privacy",
  307. Items: []string{"Public", "Private"},
  308. }
  309. _, fedStatsType, err = selPrompt.Run()
  310. if err != nil {
  311. return data, err
  312. }
  313. data.Config.App.Private = fedStatsType == "Private"
  314. }
  315. return data, Save(data.Config, fname)
  316. }