wakapi/main.go

268 lines
9.6 KiB
Go
Raw Normal View History

2019-05-05 23:36:49 +03:00
package main
2021-01-24 02:13:37 +03:00
//go:generate $GOPATH/bin/pkger
2019-05-05 23:36:49 +03:00
import (
"fmt"
"github.com/gorilla/handlers"
"github.com/markbates/pkger"
conf "github.com/muety/wakapi/config"
2020-10-16 17:11:14 +03:00
"github.com/muety/wakapi/migrations/common"
"github.com/muety/wakapi/repositories"
2019-05-06 01:58:01 +03:00
"log"
2019-05-05 23:36:49 +03:00
"net/http"
"strconv"
"time"
2019-05-06 00:23:54 +03:00
"github.com/gorilla/mux"
2020-03-31 13:22:17 +03:00
"github.com/muety/wakapi/middlewares"
customMiddleware "github.com/muety/wakapi/middlewares/custom"
2020-03-31 13:22:17 +03:00
"github.com/muety/wakapi/routes"
shieldsV1Routes "github.com/muety/wakapi/routes/compat/shields/v1"
wtV1Routes "github.com/muety/wakapi/routes/compat/wakatime/v1"
2020-03-31 13:22:17 +03:00
"github.com/muety/wakapi/services"
_ "gorm.io/driver/mysql"
_ "gorm.io/driver/postgres"
_ "gorm.io/driver/sqlite"
"gorm.io/gorm"
2019-05-05 23:36:49 +03:00
)
2020-05-24 17:34:32 +03:00
var (
db *gorm.DB
config *conf.Config
2020-05-24 17:34:32 +03:00
)
var (
aliasRepository repositories.IAliasRepository
heartbeatRepository repositories.IHeartbeatRepository
userRepository repositories.IUserRepository
languageMappingRepository repositories.ILanguageMappingRepository
summaryRepository repositories.ISummaryRepository
keyValueRepository repositories.IKeyValueRepository
)
2020-05-24 17:34:32 +03:00
var (
aliasService services.IAliasService
heartbeatService services.IHeartbeatService
userService services.IUserService
languageMappingService services.ILanguageMappingService
summaryService services.ISummaryService
aggregationService services.IAggregationService
keyValueService services.IKeyValueService
miscService services.IMiscService
2020-05-24 17:34:32 +03:00
)
2020-03-31 12:24:44 +03:00
// TODO: Refactor entire project to be structured after business domains
2019-05-05 23:36:49 +03:00
func main() {
config = conf.Load()
2020-05-24 17:34:32 +03:00
2020-04-20 02:58:54 +03:00
// Enable line numbers in logging
if config.IsDev() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
2019-05-05 23:36:49 +03:00
2019-05-11 18:49:56 +03:00
// Connect to database
2020-05-24 17:34:32 +03:00
var err error
db, err = gorm.Open(config.Db.GetDialector(), &gorm.Config{})
if config.Db.Dialect == "sqlite3" {
db.Raw("PRAGMA foreign_keys = ON;")
2020-04-20 02:58:54 +03:00
}
if config.IsDev() {
db = db.Debug()
}
sqlDb, _ := db.DB()
sqlDb.SetMaxIdleConns(int(config.Db.MaxConn))
sqlDb.SetMaxOpenConns(int(config.Db.MaxConn))
2019-05-05 23:36:49 +03:00
if err != nil {
2020-04-20 02:58:54 +03:00
log.Println(err)
log.Fatal("could not connect to database")
2019-05-05 23:36:49 +03:00
}
defer sqlDb.Close()
2019-05-11 18:49:56 +03:00
// Migrate database schema
common.RunCustomPreMigrations(db, config)
runDatabaseMigrations()
common.RunCustomPostMigrations(db, config)
2019-05-05 23:36:49 +03:00
// Repositories
aliasRepository = repositories.NewAliasRepository(db)
heartbeatRepository = repositories.NewHeartbeatRepository(db)
userRepository = repositories.NewUserRepository(db)
languageMappingRepository = repositories.NewLanguageMappingRepository(db)
summaryRepository = repositories.NewSummaryRepository(db)
keyValueRepository = repositories.NewKeyValueRepository(db)
2019-05-06 01:40:41 +03:00
// Services
aliasService = services.NewAliasService(aliasRepository)
userService = services.NewUserService(userRepository)
languageMappingService = services.NewLanguageMappingService(languageMappingRepository)
heartbeatService = services.NewHeartbeatService(heartbeatRepository, languageMappingService)
summaryService = services.NewSummaryService(summaryRepository, heartbeatService, aliasService)
aggregationService = services.NewAggregationService(userService, summaryService, heartbeatService)
keyValueService = services.NewKeyValueService(keyValueRepository)
miscService = services.NewMiscService(userService, summaryService, keyValueService)
2020-02-20 16:28:55 +03:00
// Schedule background tasks
2020-05-24 17:34:32 +03:00
go aggregationService.Schedule()
go miscService.ScheduleCountTotalTime()
2019-05-06 01:40:41 +03:00
// TODO: move endpoint registration to the respective routes files
2020-11-08 14:46:12 +03:00
routes.Init()
2019-05-06 01:40:41 +03:00
// Handlers
summaryHandler := routes.NewSummaryHandler(summaryService)
healthHandler := routes.NewHealthHandler(db)
heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService, languageMappingService)
settingsHandler := routes.NewSettingsHandler(userService, summaryService, aliasService, aggregationService, languageMappingService)
homeHandler := routes.NewHomeHandler(keyValueService)
2020-11-28 22:23:40 +03:00
loginHandler := routes.NewLoginHandler(userService)
2020-11-06 23:19:54 +03:00
imprintHandler := routes.NewImprintHandler(keyValueService)
wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(summaryService)
wakatimeV1SummariesHandler := wtV1Routes.NewSummariesHandler(summaryService)
shieldV1BadgeHandler := shieldsV1Routes.NewBadgeHandler(summaryService, userService)
2019-05-06 01:40:41 +03:00
// Setup Routers
2019-05-06 00:23:54 +03:00
router := mux.NewRouter()
publicRouter := router.PathPrefix("/").Subrouter()
settingsRouter := publicRouter.PathPrefix("/settings").Subrouter()
summaryRouter := publicRouter.PathPrefix("/summary").Subrouter()
apiRouter := router.PathPrefix("/api").Subrouter()
compatRouter := apiRouter.PathPrefix("/compat").Subrouter()
wakatimeV1Router := compatRouter.PathPrefix("/wakatime/v1").Subrouter()
shieldsV1Router := compatRouter.PathPrefix("/shields/v1").Subrouter()
2019-05-06 00:23:54 +03:00
// Middlewares
recoveryMiddleware := handlers.RecoveryHandler()
loggingMiddleware := middlewares.NewLoggingMiddleware().Handler
corsMiddleware := handlers.CORS()
authenticateMiddleware := middlewares.NewAuthenticateMiddleware(
2020-05-24 17:34:32 +03:00
userService,
[]string{"/api/health", "/api/compat/shields/v1"},
).Handler
wakatimeRelayMiddleware := customMiddleware.NewWakatimeRelayMiddleware().Handler
// Router configs
router.Use(loggingMiddleware, recoveryMiddleware)
summaryRouter.Use(authenticateMiddleware)
settingsRouter.Use(authenticateMiddleware)
apiRouter.Use(corsMiddleware, authenticateMiddleware)
// Public Routes
2020-11-06 23:19:54 +03:00
publicRouter.Path("/").Methods(http.MethodGet).HandlerFunc(homeHandler.GetIndex)
2020-11-28 22:23:40 +03:00
publicRouter.Path("/login").Methods(http.MethodGet).HandlerFunc(loginHandler.GetIndex)
publicRouter.Path("/login").Methods(http.MethodPost).HandlerFunc(loginHandler.PostLogin)
publicRouter.Path("/logout").Methods(http.MethodPost).HandlerFunc(loginHandler.PostLogout)
publicRouter.Path("/signup").Methods(http.MethodGet).HandlerFunc(loginHandler.GetSignup)
publicRouter.Path("/signup").Methods(http.MethodPost).HandlerFunc(loginHandler.PostSignup)
2020-11-06 23:19:54 +03:00
publicRouter.Path("/imprint").Methods(http.MethodGet).HandlerFunc(imprintHandler.GetImprint)
// Summary Routes
summaryRouter.Methods(http.MethodGet).HandlerFunc(summaryHandler.GetIndex)
// Settings Routes
settingsRouter.Methods(http.MethodGet).HandlerFunc(settingsHandler.GetIndex)
settingsRouter.Path("/credentials").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCredentials)
settingsRouter.Path("/aliases").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostAlias)
settingsRouter.Path("/aliases/delete").Methods(http.MethodPost).HandlerFunc(settingsHandler.DeleteAlias)
settingsRouter.Path("/language_mappings").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostLanguageMapping)
settingsRouter.Path("/language_mappings/delete").Methods(http.MethodPost).HandlerFunc(settingsHandler.DeleteLanguageMapping)
settingsRouter.Path("/reset").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostResetApiKey)
settingsRouter.Path("/badges").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostToggleBadges)
settingsRouter.Path("/wakatime_integration").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostSetWakatimeApiKey)
settingsRouter.Path("/regenerate").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostRegenerateSummaries)
2020-02-20 16:28:55 +03:00
2019-05-06 00:23:54 +03:00
// API Routes
2020-03-31 12:24:44 +03:00
apiRouter.Path("/summary").Methods(http.MethodGet).HandlerFunc(summaryHandler.ApiGet)
2020-04-08 22:29:11 +03:00
apiRouter.Path("/health").Methods(http.MethodGet).HandlerFunc(healthHandler.ApiGet)
2019-05-06 00:23:54 +03:00
heartbeatsApiRouter := apiRouter.Path("/heartbeat").Methods(http.MethodPost).Subrouter()
heartbeatsApiRouter.Use(wakatimeRelayMiddleware)
heartbeatsApiRouter.Path("").HandlerFunc(heartbeatHandler.ApiPost)
// Wakatime compat V1 API Routes
wakatimeV1Router.Path("/users/{user}/all_time_since_today").Methods(http.MethodGet).HandlerFunc(wakatimeV1AllHandler.ApiGet)
wakatimeV1Router.Path("/users/{user}/summaries").Methods(http.MethodGet).HandlerFunc(wakatimeV1SummariesHandler.ApiGet)
// Shields.io compat API Routes
shieldsV1Router.PathPrefix("/{user}").Methods(http.MethodGet).HandlerFunc(shieldV1BadgeHandler.ApiGet)
2020-02-20 16:28:55 +03:00
// Static Routes
2021-01-24 02:13:37 +03:00
router.PathPrefix("/assets").Handler(http.FileServer(pkger.Dir("/static")))
2020-02-21 14:41:29 +03:00
2019-05-05 23:36:49 +03:00
// Listen HTTP
listen(router)
}
func listen(handler http.Handler) {
var s4, s6 *http.Server
// IPv4
if config.Server.ListenIpV4 != "" {
bindString4 := config.Server.ListenIpV4 + ":" + strconv.Itoa(config.Server.Port)
s4 = &http.Server{
Handler: handler,
Addr: bindString4,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
}
// IPv6
if config.Server.ListenIpV6 != "" {
bindString6 := "[" + config.Server.ListenIpV6 + "]:" + strconv.Itoa(config.Server.Port)
s6 = &http.Server{
Handler: handler,
Addr: bindString6,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
2019-05-05 23:36:49 +03:00
}
if config.UseTLS() {
if s4 != nil {
fmt.Printf("Listening for HTTPS on %s.\n", s4.Addr)
2021-01-05 13:28:51 +03:00
go func() {
if err := s4.ListenAndServeTLS(config.Server.TlsCertPath, config.Server.TlsKeyPath); err != nil {
log.Fatalln(err)
}
}()
}
if s6 != nil {
fmt.Printf("Listening for HTTPS on %s.\n", s6.Addr)
2021-01-05 13:28:51 +03:00
go func() {
if err := s6.ListenAndServeTLS(config.Server.TlsCertPath, config.Server.TlsKeyPath); err != nil {
log.Fatalln(err)
}
}()
}
} else {
if s4 != nil {
fmt.Printf("Listening for HTTP on %s.\n", s4.Addr)
2021-01-05 13:28:51 +03:00
go func() {
if err := s4.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}()
}
if s6 != nil {
fmt.Printf("Listening for HTTP on %s.\n", s6.Addr)
2021-01-05 13:28:51 +03:00
go func() {
if err := s6.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}()
}
}
<-make(chan interface{}, 1)
2019-05-05 23:36:49 +03:00
}
2019-05-21 23:01:14 +03:00
func runDatabaseMigrations() {
if err := config.GetMigrationFunc(config.Db.Dialect)(db); err != nil {
log.Fatal(err)
}
}