GraphQL API and utilities for the rpdata project
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.

150 lines
4.1 KiB

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "log"
  6. "net/http"
  7. "git.aiterp.net/rpdata/api/graphql/loader"
  8. "git.aiterp.net/rpdata/api/graphql/resolver"
  9. "git.aiterp.net/rpdata/api/graphql/schema"
  10. "git.aiterp.net/rpdata/api/internal/auth"
  11. "git.aiterp.net/rpdata/api/internal/store"
  12. "git.aiterp.net/rpdata/api/model/change"
  13. "git.aiterp.net/rpdata/api/model/file"
  14. logModel "git.aiterp.net/rpdata/api/model/log"
  15. graphql "github.com/graph-gophers/graphql-go"
  16. "github.com/graph-gophers/graphql-go/relay"
  17. )
  18. func main() {
  19. err := store.Init()
  20. if err != nil {
  21. log.Fatalln("Failed to init store:", err)
  22. }
  23. n, err := logModel.UpdateAllCharacters()
  24. if err != nil {
  25. log.Println("Charcter updated stopped:", err)
  26. }
  27. log.Println("Updated characters on", n, "logs")
  28. schema, err := graphql.ParseSchema(schema.String(), &resolver.Resolver{}, graphql.MaxParallelism(48))
  29. if err != nil {
  30. log.Fatalln("Failed to parse schema:", err)
  31. }
  32. http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  33. if r.Method != "GET" {
  34. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  35. return
  36. }
  37. w.Write(page)
  38. }))
  39. relayHandler := &relay.Handler{Schema: schema}
  40. http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
  41. l := loader.New()
  42. r = r.WithContext(l.ToContext(r.Context()))
  43. r = auth.RequestWithToken(r)
  44. relayHandler.ServeHTTP(w, r)
  45. })
  46. http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
  47. r = auth.RequestWithToken(r)
  48. token := auth.TokenFromContext(r.Context())
  49. if !token.Permitted("file.upload") {
  50. http.Error(w, "Unauthorized", http.StatusUnauthorized)
  51. return
  52. }
  53. if err := r.ParseMultipartForm(16384); err != nil {
  54. http.Error(w, "Internal (1): "+err.Error(), http.StatusInternalServerError)
  55. return
  56. }
  57. formFile, header, err := r.FormFile("file")
  58. if err != nil || header == nil || formFile == nil {
  59. http.Error(w, "No file provided", http.StatusBadRequest)
  60. return
  61. }
  62. file, err := file.Upload(r.Context(), header.Filename, header.Header.Get("Content-Type"), token.UserID, header.Size, formFile)
  63. if err != nil {
  64. http.Error(w, "Internal (2): "+err.Error(), http.StatusInternalServerError)
  65. return
  66. }
  67. json, err := json.Marshal(file)
  68. if err != nil {
  69. http.Error(w, "Internal (3): "+err.Error(), http.StatusInternalServerError)
  70. return
  71. }
  72. w.Header().Set("Content-Type", "application/json")
  73. w.WriteHeader(http.StatusOK)
  74. w.Write(json)
  75. })
  76. change.InitializeTimeline()
  77. go func() {
  78. index := int64(0)
  79. for {
  80. index++
  81. ch, err := change.Timeline.Get(context.Background(), index)
  82. if err == change.ErrExpired {
  83. continue
  84. } else if err != nil {
  85. log.Println("Timeline:", err)
  86. break
  87. }
  88. log.Printf("Change: time=%s model=%s op=%s id=%s author=%s\n", ch.Time.Local().Format("[Mon Jan 02 15:04 MST]"), ch.Model, ch.Op, ch.ObjectID, ch.Author)
  89. }
  90. }()
  91. log.Fatal(http.ListenAndServe(":17000", nil))
  92. }
  93. var page = []byte(`
  94. <!DOCTYPE html>
  95. <html>
  96. <head>
  97. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.css" />
  98. <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.1.0/fetch.min.js"></script>
  99. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js"></script>
  100. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js"></script>
  101. <script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.js"></script>
  102. </head>
  103. <body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
  104. <div id="graphiql" style="height: 100vh;">Loading...</div>
  105. <script>
  106. function graphQLFetcher(graphQLParams) {
  107. return fetch("/graphql", {
  108. method: "post",
  109. body: JSON.stringify(graphQLParams),
  110. credentials: "include",
  111. }).then(function (response) {
  112. return response.text();
  113. }).then(function (responseBody) {
  114. try {
  115. return JSON.parse(responseBody);
  116. } catch (error) {
  117. return responseBody;
  118. }
  119. });
  120. }
  121. ReactDOM.render(
  122. React.createElement(GraphiQL, {fetcher: graphQLFetcher}),
  123. document.getElementById("graphiql")
  124. );
  125. </script>
  126. </body>
  127. </html>
  128. `)