示例#1
0
func main() {
	http.Handle("/", http.FileServer(http.FileSystem(public)))
}
示例#2
0
// Minimum number of items to request at a time from wrapped Readdir.
const batchSize = 100

// Order is a http.FileSystem wrapper that strives to list directory
// contents in alphabetical order.
//
// To limit memory consumption, if the directory is larger than
// WindowSize, the Readdir results will only approximate the correct
// order. The listing will contain runs of entries in sorted order,
// where the runs are broken only when entries are seen further than
// WindowSize from their desired ordered location.
type Order struct {
	http.FileSystem
}

var _ = http.FileSystem(Order{})

// Open opens a file. See http.FileSystem method Open.
func (o Order) Open(name string) (http.File, error) {
	f, err := o.FileSystem.Open(name)
	if f != nil {
		f = &file{File: f}
	}
	return f, err
}

type file struct {
	http.File
	window fileHeap
	// if not nil, we're in draining mode, and will return this once
	// window is empty
示例#3
0
func main() {
	// Configure logging: http://godoc.org/github.com/op/go-logging#Formatter
	log = &CustomLogger{
		Logger: logging.MustGetLogger("isaac"),
	}
	loggingBackend := logging.NewLogBackend(os.Stdout, "", 0)
	logFormat := logging.MustStringFormatter( // https://golang.org/pkg/time/#Time.Format
		//`%{time:Mon Jan 2 15:04:05 MST 2006} - %{level:.4s} - %{shortfile} - %{message}`, // We no longer use the line number since the log struct extension breaks it
		`%{time:Mon Jan 2 15:04:05 MST 2006} - %{level:.4s} - %{message}`,
	)
	loggingBackendFormatted := logging.NewBackendFormatter(loggingBackend, logFormat)
	logging.SetBackend(loggingBackendFormatted)

	// Load the .env file which contains environment variables with secret values
	err := godotenv.Load(projectPath + "/.env")
	if err != nil {
		log.Fatal("Failed to load .env file:", err)
	}

	// Configure error reporting to Sentry
	sentrySecret := os.Getenv("SENTRY_SECRET")
	raven.SetDSN("https://*****:*****@sentry.io/124813")

	// Welcome message
	log.Info("-----------------------------")
	log.Info("Starting isaac-racing-server.")
	log.Info("-----------------------------")

	// Create a session store
	sessionSecret := os.Getenv("SESSION_SECRET")
	sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
	maxAge := 5 // 5 seconds
	if useSSL == true {
		sessionStore.Options = &sessions.Options{
			Domain:   domain,
			Path:     "/",
			MaxAge:   maxAge,
			Secure:   true, // Only send the cookie over HTTPS: https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002)
			HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly
		}
	} else {
		sessionStore.Options = &sessions.Options{
			Domain:   domain,
			Path:     "/",
			MaxAge:   maxAge,
			HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly
		}
	}

	// Initialize the database model
	if db, err = models.GetModels(projectPath + "/database.sqlite"); err != nil {
		log.Fatal("Failed to open the database:", err)
	}

	// Clean up any non-started races before we start
	if nonStartedRaces, err := db.Races.Cleanup(); err != nil {
		log.Fatal("Failed to cleanup the leftover races:", err)
	} else {
		for _, raceID := range nonStartedRaces {
			log.Info("Deleted race", raceID, "during starting cleanup.")
		}
	}

	// Initiate the "end of race" function for each of the non-finished races in 30 minutes
	// (this is normally initiated on race start)
	if startedRaces, err := db.Races.GetCurrentRaces(); err != nil {
		log.Fatal("Failed to start  the leftover races:", err)
	} else {
		for _, race := range startedRaces {
			go raceCheckStart3(race.ID)
		}
	}

	// Initialize the achievements
	achievementsInit()

	// Start the Twitch bot
	go twitchInit()

	// Create a WebSocket router using the Golem framework
	router := golem.NewRouter()
	router.SetConnectionExtension(NewExtendedConnection)
	router.OnHandshake(validateSession)
	router.OnConnect(connOpen)
	router.OnClose(connClose)

	/*
		The websocket commands
	*/

	// Chat commands
	router.On("roomJoin", roomJoin)
	router.On("roomLeave", roomLeave)
	router.On("roomMessage", roomMessage)
	router.On("privateMessage", privateMessage)
	router.On("roomListAll", roomListAll)

	// Race commands
	router.On("raceCreate", raceCreate)
	router.On("raceJoin", raceJoin)
	router.On("raceLeave", raceLeave)
	router.On("raceReady", raceReady)
	router.On("raceUnready", raceUnready)
	router.On("raceRuleset", raceRuleset)
	router.On("raceFinish", raceFinish)
	router.On("raceQuit", raceQuit)
	router.On("raceComment", raceComment)
	router.On("raceItem", raceItem)
	router.On("raceFloor", raceFloor)

	// Profile commands
	router.On("profileGet", profileGet)
	router.On("profileSetUsername", profileSetUsername)
	router.On("profileSetStreamURL", profileSetStreamURL)
	router.On("profileSetTwitchBotEnabled", profileSetTwitchBotEnabled)
	router.On("profileSetTwitchBotDelay", profileSetTwitchBotDelay)

	// Admin commands
	router.On("adminBan", adminBan)
	router.On("adminUnban", adminUnban)
	router.On("adminBanIP", adminBanIP)
	router.On("adminUnbanIP", adminUnbanIP)
	router.On("adminMute", adminMute)
	router.On("adminUnmute", adminUnmute)
	router.On("adminPromote", adminPromote)
	router.On("adminDemote", adminDemote)

	// Miscellaneous
	router.On("logout", logout)
	router.On("debug", debug)

	/*
		HTTP stuff
	*/

	// Minify CSS and JS
	m := minify.New()
	m.AddFunc("text/css", css.Minify)
	for _, fileName := range []string{"main", "ie8"} {
		inputFile, _ := os.Open("public/css/" + fileName + ".css")
		outputFile, _ := os.Create("public/css/" + fileName + ".min.css")
		if err := m.Minify("text/css", outputFile, inputFile); err != nil {
			log.Error("Failed to minify \""+fileName+".css\":", err)
		}
	}
	m.AddFunc("text/javascript", js.Minify)
	for _, fileName := range []string{"main", "util"} {
		inputFile, _ := os.Open("public/js/" + fileName + ".js")
		outputFile, _ := os.Create("public/js/" + fileName + ".min.js")
		if err := m.Minify("text/javascript", outputFile, inputFile); err != nil {
			log.Error("Failed to minify \""+fileName+".js\":", err)
		}
	}

	// Set up the Pat HTTP router
	p := pat.New()
	p.Get("/", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpHome))
	p.Get("/news", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpNews))
	p.Get("/races", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpRaces))
	p.Get("/profiles", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpProfiles))
	p.Get("/leaderboards", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpLeaderboards))
	p.Get("/info", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpInfo))
	p.Get("/download", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpDownload))
	p.Post("/login", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), loginHandler))
	p.Post("/register", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), registerHandler))

	/*
		Assign functions to URIs
	*/

	// Normal requests get assigned to the Pat HTTP router
	http.Handle("/", p)

	// Files in the "public" subdirectory are just images/css/javascript files
	http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(justFilesFilesystem{http.Dir("public")})))

	// Websockets are handled by the Golem websocket router
	http.HandleFunc("/ws", router.Handler())

	/*
		Start the server
	*/

	// Figure out the port that we are using for the HTTP server
	var port int
	if useSSL == true {
		// We want all HTTP requests to be redirected, but we need to make an exception for Let's Encrypt
		// The previous "Handle" and "HandleFunc" were being added to the default serve mux
		// We need to create a new fresh one for the HTTP handler
		HTTPServeMux := http.NewServeMux()
		HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir("letsencrypt"))))
		HTTPServeMux.Handle("/", http.HandlerFunc(HTTPRedirect))

		// ListenAndServe is blocking, so start listening on a new goroutine
		go http.ListenAndServe(":80", HTTPServeMux) // Nothing before the colon implies 0.0.0.0

		// 443 is the default port for HTTPS
		port = 443
	} else {
		// 80 is the defeault port for HTTP
		port = 80
	}

	// Listen and serve
	log.Info("Listening on port " + strconv.Itoa(port) + ".")
	if useSSL == true {
		if err := http.ListenAndServeTLS(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			sslCertFile,
			sslKeyFile,
			context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions
		); err != nil {
			log.Fatal("ListenAndServeTLS failed:", err)
		}
	} else {
		// Listen and serve (HTTP)
		if err := http.ListenAndServe(
			":"+strconv.Itoa(port),                     // Nothing before the colon implies 0.0.0.0
			context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions
		); err != nil {
			log.Fatal("ListenAndServeTLS failed:", err)
		}
	}
}