github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/cmd/install/install.go (about) 1 /* 2 * 3 * Gosora Installer 4 * Copyright Azareal 2017 - 2019 5 * 6 */ 7 package main 8 9 import ( 10 "bufio" 11 "errors" 12 "fmt" 13 "os" 14 "runtime/debug" 15 "strconv" 16 "strings" 17 18 "github.com/Azareal/Gosora/install" 19 ) 20 21 var scanner *bufio.Scanner 22 23 var siteShortName string 24 var siteName string 25 var siteURL string 26 var serverPort string 27 28 var defaultAdapter = "mysql" 29 var defaultHost = "localhost" 30 var defaultUsername = "root" 31 var defaultDbname = "gosora" 32 var defaultSiteShortName = "SN" 33 var defaultSiteName = "Site Name" 34 var defaultsiteURL = "localhost" 35 var defaultServerPort = "80" // 8080's a good one, if you're testing and don't want it to clash with port 80 36 37 func main() { 38 // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows 39 defer func() { 40 if r := recover(); r != nil { 41 fmt.Println(r) 42 debug.PrintStack() 43 pressAnyKey() 44 return 45 } 46 }() 47 48 scanner = bufio.NewScanner(os.Stdin) 49 fmt.Println("Welcome to Gosora's Installer") 50 fmt.Println("We're going to take you through a few steps to help you get started :)") 51 adap, ok := handleDatabaseDetails() 52 if !ok { 53 err := scanner.Err() 54 if err != nil { 55 fmt.Println(err) 56 } else { 57 err = errors.New("Something went wrong!") 58 } 59 abortError(err) 60 return 61 } 62 63 if !getSiteDetails() { 64 err := scanner.Err() 65 if err != nil { 66 fmt.Println(err) 67 } else { 68 err = errors.New("Something went wrong!") 69 } 70 abortError(err) 71 return 72 } 73 74 err := adap.InitDatabase() 75 if err != nil { 76 abortError(err) 77 return 78 } 79 80 err = adap.TableDefs() 81 if err != nil { 82 abortError(err) 83 return 84 } 85 86 err = adap.CreateAdmin() 87 if err != nil { 88 abortError(err) 89 return 90 } 91 92 err = adap.InitialData() 93 if err != nil { 94 abortError(err) 95 return 96 } 97 98 configContents := []byte(`{ 99 "Site": { 100 "ShortName":"` + siteShortName + `", 101 "Name":"` + siteName + `", 102 "URL":"` + siteURL + `", 103 "Port":"` + serverPort + `", 104 "EnableSsl":false, 105 "EnableEmails":false, 106 "HasProxy":false, 107 "Language": "english" 108 }, 109 "Config": { 110 "SslPrivkey": "", 111 "SslFullchain": "", 112 "SMTPServer": "", 113 "SMTPUsername": "", 114 "SMTPPassword": "", 115 "SMTPPort": "25", 116 117 "MaxRequestSizeStr":"5MB", 118 "UserCache":"static", 119 "TopicCache":"static", 120 "ReplyCache":"static", 121 "UserCacheCapacity":180, 122 "TopicCacheCapacity":400, 123 "ReplyCacheCapacity":20, 124 "DefaultPath":"/topics/", 125 "DefaultGroup":3, 126 "ActivationGroup":5, 127 "StaffCSS":"staff_post", 128 "DefaultForum":2, 129 "MinifyTemplates":true, 130 "BuildSlugs":true, 131 "ServerCount":1, 132 "Noavatar":"https://api.adorable.io/avatars/{width}/{id}.png", 133 "ItemsPerPage":25 134 }, 135 "Database": { 136 "Adapter": "` + adap.Name() + `", 137 "Host": "` + adap.DBHost() + `", 138 "Username": "` + adap.DBUsername() + `", 139 "Password": "` + adap.DBPassword() + `", 140 "Dbname": "` + adap.DBName() + `", 141 "Port": "` + adap.DBPort() + `", 142 143 "TestAdapter": "` + adap.Name() + `", 144 "TestHost": "", 145 "TestUsername": "", 146 "TestPassword": "", 147 "TestDbname": "", 148 "TestPort": "" 149 }, 150 "Dev": { 151 "DebugMode":true, 152 "SuperDebug":false 153 } 154 }`) 155 156 fmt.Println("Opening the configuration file") 157 configFile, err := os.Create("./config/config.json") 158 if err != nil { 159 abortError(err) 160 return 161 } 162 163 fmt.Println("Writing to the configuration file...") 164 _, err = configFile.Write(configContents) 165 if err != nil { 166 abortError(err) 167 return 168 } 169 170 configFile.Sync() 171 configFile.Close() 172 fmt.Println("Finished writing to the configuration file") 173 174 fmt.Println("Yay, you have successfully installed Gosora!") 175 fmt.Println("Your name is Admin and you can login with the password 'password'. Don't forget to change it! Seriously. It's really insecure.") 176 pressAnyKey() 177 } 178 179 func abortError(err error) { 180 fmt.Println(err) 181 fmt.Println("Aborting installation...") 182 pressAnyKey() 183 } 184 185 func handleDatabaseDetails() (adap install.InstallAdapter, ok bool) { 186 var dbAdapter string 187 var dbHost string 188 var dbUsername string 189 var dbPassword string 190 var dbName string 191 // TODO: Let the admin set the database port? 192 //var dbPort string 193 194 for { 195 fmt.Println("Which database adapter do you wish to use? mysql or mssql? Default: mysql") 196 if !scanner.Scan() { 197 return nil, false 198 } 199 dbAdapter := strings.TrimSpace(scanner.Text()) 200 if dbAdapter == "" { 201 dbAdapter = defaultAdapter 202 } 203 adap, ok = install.Lookup(dbAdapter) 204 if ok { 205 break 206 } 207 fmt.Println("That adapter doesn't exist") 208 } 209 fmt.Println("Set database adapter to " + dbAdapter) 210 211 fmt.Println("Database Host? Default: " + defaultHost) 212 if !scanner.Scan() { 213 return nil, false 214 } 215 dbHost = scanner.Text() 216 if dbHost == "" { 217 dbHost = defaultHost 218 } 219 fmt.Println("Set database host to " + dbHost) 220 221 fmt.Println("Database Username? Default: " + defaultUsername) 222 if !scanner.Scan() { 223 return nil, false 224 } 225 dbUsername = scanner.Text() 226 if dbUsername == "" { 227 dbUsername = defaultUsername 228 } 229 fmt.Println("Set database username to " + dbUsername) 230 231 fmt.Println("Database Password? Default: ''") 232 if !scanner.Scan() { 233 return nil, false 234 } 235 dbPassword = scanner.Text() 236 if len(dbPassword) == 0 { 237 fmt.Println("You didn't set a password for this user. This won't block the installation process, but it might create security issues in the future.") 238 fmt.Println("") 239 } else { 240 fmt.Println("Set password to " + obfuscatePassword(dbPassword)) 241 } 242 243 fmt.Println("Database Name? Pick a name you like or one provided to you. Default: " + defaultDbname) 244 if !scanner.Scan() { 245 return nil, false 246 } 247 dbName = scanner.Text() 248 if dbName == "" { 249 dbName = defaultDbname 250 } 251 fmt.Println("Set database name to " + dbName) 252 253 adap.SetConfig(dbHost, dbUsername, dbPassword, dbName, adap.DefaultPort()) 254 return adap, true 255 } 256 257 func getSiteDetails() bool { 258 fmt.Println("Okay. We also need to know some actual information about your site!") 259 fmt.Println("What's your site's name? Default: " + defaultSiteName) 260 if !scanner.Scan() { 261 return false 262 } 263 siteName = scanner.Text() 264 if siteName == "" { 265 siteName = defaultSiteName 266 } 267 fmt.Println("Set the site name to " + siteName) 268 269 // ? - We could compute this based on the first letter of each word in the site's name, if it's name spans multiple words. I'm not sure how to do this for single word names. 270 fmt.Println("Can we have a short abbreviation for your site? Default: " + defaultSiteShortName) 271 if !scanner.Scan() { 272 return false 273 } 274 siteShortName = scanner.Text() 275 if siteShortName == "" { 276 siteShortName = defaultSiteShortName 277 } 278 fmt.Println("Set the short name to " + siteShortName) 279 280 fmt.Println("What's your site's url? Default: " + defaultsiteURL) 281 if !scanner.Scan() { 282 return false 283 } 284 siteURL = scanner.Text() 285 if siteURL == "" { 286 siteURL = defaultsiteURL 287 } 288 fmt.Println("Set the site url to " + siteURL) 289 290 fmt.Println("What port do you want the server to listen on? If you don't know what this means, you should probably leave it on the default. Default: " + defaultServerPort) 291 if !scanner.Scan() { 292 return false 293 } 294 serverPort = scanner.Text() 295 if serverPort == "" { 296 serverPort = defaultServerPort 297 } 298 _, err := strconv.Atoi(serverPort) 299 if err != nil { 300 fmt.Println("That's not a valid number!") 301 return false 302 } 303 fmt.Println("Set the server port to " + serverPort) 304 return true 305 } 306 307 func obfuscatePassword(password string) (out string) { 308 for i := 0; i < len(password); i++ { 309 out += "*" 310 } 311 return out 312 } 313 314 func pressAnyKey() { 315 //fmt.Println("Press any key to exit...") 316 fmt.Println("Please press enter to exit...") 317 for scanner.Scan() { 318 _ = scanner.Text() 319 return 320 } 321 }