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/internal/session"
  8. "git.aiterp.net/rpdata/api/internal/store"
  9. "git.aiterp.net/rpdata/api/loader"
  10. "git.aiterp.net/rpdata/api/model/change"
  11. "git.aiterp.net/rpdata/api/model/file"
  12. logModel "git.aiterp.net/rpdata/api/model/log"
  13. "git.aiterp.net/rpdata/api/resolver"
  14. "git.aiterp.net/rpdata/api/schema"
  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.RootResolver{}, 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. r = session.Load(w, r)
  42. l := loader.New()
  43. r = r.WithContext(l.ToContext(r.Context()))
  44. relayHandler.ServeHTTP(w, r)
  45. })
  46. http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
  47. r = session.Load(w, r)
  48. sess := session.FromContext(r.Context())
  49. user := sess.User()
  50. if user == nil || !user.Permitted("file.upload") {
  51. http.Error(w, "Unauthorized", http.StatusUnauthorized)
  52. return
  53. }
  54. if err := r.ParseMultipartForm(16384); err != nil {
  55. http.Error(w, "Internal (1): "+err.Error(), http.StatusInternalServerError)
  56. return
  57. }
  58. formFile, header, err := r.FormFile("file")
  59. if err != nil || header == nil || formFile == nil {
  60. http.Error(w, "No file provided", http.StatusBadRequest)
  61. return
  62. }
  63. file, err := file.Upload(r.Context(), header.Filename, header.Header.Get("Content-Type"), user.ID, header.Size, formFile)
  64. if err != nil {
  65. http.Error(w, "Internal (2): "+err.Error(), http.StatusInternalServerError)
  66. return
  67. }
  68. json, err := json.Marshal(file)
  69. if err != nil {
  70. http.Error(w, "Internal (3): "+err.Error(), http.StatusInternalServerError)
  71. return
  72. }
  73. w.Header().Set("Content-Type", "application/json")
  74. w.WriteHeader(http.StatusOK)
  75. w.Write(json)
  76. })
  77. change.InitializeTimeline()
  78. go func() {
  79. index := int64(0)
  80. for {
  81. index++
  82. ch, err := change.Timeline.Get(context.Background(), index)
  83. if err == change.ErrExpired {
  84. continue
  85. } else if err != nil {
  86. log.Println("Timeline:", err)
  87. break
  88. }
  89. 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)
  90. }
  91. }()
  92. log.Fatal(http.ListenAndServe(":17000", nil))
  93. }
  94. var page = []byte(`
  95. <!DOCTYPE html>
  96. <html>
  97. <head>
  98. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.css" />
  99. <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.1.0/fetch.min.js"></script>
  100. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js"></script>
  101. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js"></script>
  102. <script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.js"></script>
  103. </head>
  104. <body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
  105. <div id="graphiql" style="height: 100vh;">Loading...</div>
  106. <script>
  107. function graphQLFetcher(graphQLParams) {
  108. return fetch("/graphql", {
  109. method: "post",
  110. body: JSON.stringify(graphQLParams),
  111. credentials: "include",
  112. }).then(function (response) {
  113. return response.text();
  114. }).then(function (responseBody) {
  115. try {
  116. return JSON.parse(responseBody);
  117. } catch (error) {
  118. return responseBody;
  119. }
  120. });
  121. }
  122. ReactDOM.render(
  123. React.createElement(GraphiQL, {fetcher: graphQLFetcher}),
  124. document.getElementById("graphiql")
  125. );
  126. </script>
  127. </body>
  128. </html>
  129. `)