stufflog graphql server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
4.2 KiB

  1. package main
  2. import (
  3. "context"
  4. "git.aiterp.net/stufflog/server/database"
  5. "git.aiterp.net/stufflog/server/graph"
  6. "git.aiterp.net/stufflog/server/internal/generate"
  7. "git.aiterp.net/stufflog/server/internal/xlerrors"
  8. "git.aiterp.net/stufflog/server/models"
  9. "git.aiterp.net/stufflog/server/services"
  10. "github.com/99designs/gqlgen/graphql/playground"
  11. "github.com/gin-gonic/gin"
  12. "github.com/pkg/errors"
  13. "github.com/pressly/goose"
  14. "github.com/urfave/cli/v2"
  15. "log"
  16. "os"
  17. "os/signal"
  18. "sort"
  19. "syscall"
  20. "time"
  21. )
  22. func main() {
  23. app := &cli.App{
  24. Name: "stufflog",
  25. Usage: "Issue tracker for your home and hobbies",
  26. Flags: []cli.Flag{
  27. &cli.StringFlag{
  28. Name: "db-driver",
  29. Value: "mysql",
  30. Usage: "Database driver",
  31. EnvVars: []string{"DATABASE_DRIVER"},
  32. },
  33. &cli.StringFlag{
  34. Name: "db-connect",
  35. Value: "stufflog_user:stuff1234@(localhost:3306)/stufflog",
  36. Usage: "Database connection string or path",
  37. EnvVars: []string{"DATABASE_CONNECT"},
  38. },
  39. &cli.StringFlag{
  40. Name: "listen",
  41. Value: ":8000",
  42. Usage: "Address to bind the server to.",
  43. EnvVars: []string{"SERVER_LISTEN"},
  44. },
  45. },
  46. Commands: []*cli.Command{
  47. {
  48. Name: "reset-admin",
  49. Usage: "Reset the admin user (or create it)",
  50. Action: func(c *cli.Context) error {
  51. db, err := database.Open(c.String("db-driver"), c.String("db-connect"))
  52. if err != nil {
  53. return errors.Wrap(err, "Failed to connect to database")
  54. }
  55. timeout, cancel := context.WithTimeout(context.Background(), time.Second*5)
  56. defer cancel()
  57. newPass := generate.Generate(20, "")
  58. admin, err := db.Users().Find(timeout, "Admin")
  59. if xlerrors.IsNotFound(err) {
  60. admin = &models.User{
  61. ID: "Admin",
  62. Name: "Administrator",
  63. Active: true,
  64. Admin: true,
  65. }
  66. err = admin.SetPassword(newPass)
  67. if err != nil {
  68. return errors.Wrap(err, "Failed to set password")
  69. }
  70. _, err = db.Users().Insert(timeout, *admin)
  71. if err != nil {
  72. return errors.Wrap(err, "Failed to inset user")
  73. }
  74. } else if err != nil {
  75. return err
  76. } else {
  77. err = admin.SetPassword(newPass)
  78. if err != nil {
  79. return errors.Wrap(err, "Failed to set password")
  80. }
  81. err = db.Users().Save(timeout, *admin)
  82. if err != nil {
  83. return errors.Wrap(err, "Failed to save user")
  84. }
  85. }
  86. log.Println("Username:", admin.ID)
  87. log.Println("Password:", newPass)
  88. return nil
  89. },
  90. },
  91. {
  92. Name: "migrate",
  93. Usage: "Migrate the configured database",
  94. Action: func(c *cli.Context) error {
  95. db, err := database.Open(c.String("db-driver"), c.String("db-connect"))
  96. if err != nil {
  97. return errors.Wrap(err, "Failed to connect to database")
  98. }
  99. err = db.Migrate()
  100. if err == goose.ErrNoNextVersion {
  101. log.Println("No more migrations to run")
  102. } else if err != nil {
  103. return errors.Wrap(err, "Failed to run migration")
  104. }
  105. log.Println("Migration succeeded")
  106. return nil
  107. },
  108. },
  109. {
  110. Name: "server",
  111. Usage: "Run the server",
  112. Action: func(c *cli.Context) error {
  113. db, err := database.Open(c.String("db-driver"), c.String("db-connect"))
  114. if err != nil {
  115. return errors.Wrap(err, "Failed to connect to database")
  116. }
  117. bundle := services.NewBundle(db)
  118. server := gin.New()
  119. server.GET("/graphql", graph.Gin(bundle, db))
  120. server.POST("/graphql", graph.Gin(bundle, db))
  121. server.GET("/playground", gin.WrapH(playground.Handler("StuffLog GraphQL Playground", "/graphql")))
  122. exitSignal := make(chan os.Signal)
  123. signal.Notify(exitSignal, os.Interrupt, os.Kill, syscall.SIGTERM)
  124. errCh := make(chan error)
  125. go func() {
  126. err := server.Run(c.String("listen"))
  127. if err != nil {
  128. errCh <- err
  129. }
  130. }()
  131. select {
  132. case signal := <-exitSignal:
  133. {
  134. log.Println("Received signal", signal)
  135. return nil
  136. }
  137. case err := <-errCh:
  138. {
  139. return err
  140. }
  141. }
  142. },
  143. },
  144. },
  145. }
  146. sort.Sort(cli.FlagsByName(app.Flags))
  147. sort.Sort(cli.CommandsByName(app.Commands))
  148. err := app.Run(os.Args)
  149. if err != nil {
  150. log.Fatal(err)
  151. }
  152. }