nodeinfo.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package controllers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "github.com/owncast/owncast/activitypub/apmodels"
  7. "github.com/owncast/owncast/activitypub/crypto"
  8. "github.com/owncast/owncast/activitypub/persistence"
  9. "github.com/owncast/owncast/activitypub/requests"
  10. "github.com/owncast/owncast/config"
  11. "github.com/owncast/owncast/core/data"
  12. log "github.com/sirupsen/logrus"
  13. )
  14. // NodeInfoController returns the V1 node info response.
  15. func NodeInfoController(w http.ResponseWriter, r *http.Request) {
  16. type links struct {
  17. Rel string `json:"rel"`
  18. Href string `json:"href"`
  19. }
  20. type response struct {
  21. Links []links `json:"links"`
  22. }
  23. if !data.GetFederationEnabled() {
  24. w.WriteHeader(http.StatusMethodNotAllowed)
  25. return
  26. }
  27. serverURL := data.GetServerURL()
  28. if serverURL == "" {
  29. w.WriteHeader(http.StatusNotFound)
  30. return
  31. }
  32. v2, err := url.Parse(serverURL)
  33. if err != nil {
  34. w.WriteHeader(http.StatusInternalServerError)
  35. return
  36. }
  37. v2.Path = "nodeinfo/2.0"
  38. res := response{
  39. Links: []links{
  40. {
  41. Rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
  42. Href: v2.String(),
  43. },
  44. },
  45. }
  46. if err := writeResponse(res, w); err != nil {
  47. log.Errorln(err)
  48. }
  49. }
  50. // NodeInfoV2Controller returns the V2 node info response.
  51. func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
  52. type metadata struct {
  53. ChatEnabled bool `json:"chat_enabled"`
  54. }
  55. type services struct {
  56. Outbound []string `json:"outbound"`
  57. Inbound []string `json:"inbound"`
  58. }
  59. type software struct {
  60. Name string `json:"name"`
  61. Version string `json:"version"`
  62. }
  63. type users struct {
  64. Total int `json:"total"`
  65. ActiveMonth int `json:"activeMonth"`
  66. ActiveHalfyear int `json:"activeHalfyear"`
  67. }
  68. type usage struct {
  69. Users users `json:"users"`
  70. LocalPosts int `json:"localPosts"`
  71. }
  72. type response struct {
  73. Version string `json:"version"`
  74. Services services `json:"services"`
  75. Software software `json:"software"`
  76. Protocols []string `json:"protocols"`
  77. Usage usage `json:"usage"`
  78. OpenRegistrations bool `json:"openRegistrations"`
  79. Metadata metadata `json:"metadata"`
  80. }
  81. if !data.GetFederationEnabled() {
  82. w.WriteHeader(http.StatusMethodNotAllowed)
  83. return
  84. }
  85. localPostCount, _ := persistence.GetLocalPostCount()
  86. res := response{
  87. Version: "2.0",
  88. Services: services{
  89. Inbound: []string{},
  90. Outbound: []string{},
  91. },
  92. Software: software{
  93. Name: "owncast",
  94. Version: config.VersionNumber,
  95. },
  96. Usage: usage{
  97. Users: users{
  98. Total: 1,
  99. ActiveMonth: 1,
  100. ActiveHalfyear: 1,
  101. },
  102. LocalPosts: int(localPostCount),
  103. },
  104. OpenRegistrations: false,
  105. Protocols: []string{"activitypub"},
  106. Metadata: metadata{
  107. ChatEnabled: !data.GetChatDisabled(),
  108. },
  109. }
  110. if err := writeResponse(res, w); err != nil {
  111. log.Errorln(err)
  112. }
  113. }
  114. // XNodeInfo2Controller returns the x-nodeinfo2.
  115. func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
  116. type Organization struct {
  117. Name string `json:"name"`
  118. Contact string `json:"contact"`
  119. }
  120. type Server struct {
  121. BaseURL string `json:"baseUrl"`
  122. Version string `json:"version"`
  123. Name string `json:"name"`
  124. Software string `json:"software"`
  125. }
  126. type Services struct {
  127. Outbound []string `json:"outbound"`
  128. Inbound []string `json:"inbound"`
  129. }
  130. type Users struct {
  131. ActiveWeek int `json:"activeWeek"`
  132. Total int `json:"total"`
  133. ActiveMonth int `json:"activeMonth"`
  134. ActiveHalfyear int `json:"activeHalfyear"`
  135. }
  136. type Usage struct {
  137. Users Users `json:"users"`
  138. LocalPosts int `json:"localPosts"`
  139. LocalComments int `json:"localComments"`
  140. }
  141. type response struct {
  142. Server Server `json:"server"`
  143. Organization Organization `json:"organization"`
  144. Version string `json:"version"`
  145. Services Services `json:"services"`
  146. Protocols []string `json:"protocols"`
  147. Usage Usage `json:"usage"`
  148. OpenRegistrations bool `json:"openRegistrations"`
  149. }
  150. if !data.GetFederationEnabled() {
  151. w.WriteHeader(http.StatusMethodNotAllowed)
  152. return
  153. }
  154. serverURL := data.GetServerURL()
  155. if serverURL == "" {
  156. w.WriteHeader(http.StatusNotFound)
  157. return
  158. }
  159. localPostCount, _ := persistence.GetLocalPostCount()
  160. res := &response{
  161. Organization: Organization{
  162. Name: data.GetServerName(),
  163. Contact: serverURL,
  164. },
  165. Server: Server{
  166. BaseURL: serverURL,
  167. Version: config.VersionNumber,
  168. Name: "owncast",
  169. Software: "owncast",
  170. },
  171. Services: Services{
  172. Inbound: []string{"activitypub"},
  173. Outbound: []string{"activitypub"},
  174. },
  175. Protocols: []string{"activitypub"},
  176. Version: config.VersionNumber,
  177. Usage: Usage{
  178. Users: Users{
  179. ActiveWeek: 1,
  180. Total: 1,
  181. ActiveMonth: 1,
  182. ActiveHalfyear: 1,
  183. },
  184. LocalPosts: int(localPostCount),
  185. LocalComments: 0,
  186. },
  187. }
  188. if err := writeResponse(res, w); err != nil {
  189. log.Errorln(err)
  190. }
  191. }
  192. // InstanceV1Controller returns the v1 instance details.
  193. func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
  194. type Stats struct {
  195. UserCount int `json:"user_count"`
  196. StatusCount int `json:"status_count"`
  197. DomainCount int `json:"domain_count"`
  198. }
  199. type response struct {
  200. URI string `json:"uri"`
  201. Title string `json:"title"`
  202. ShortDescription string `json:"short_description"`
  203. Description string `json:"description"`
  204. Version string `json:"version"`
  205. Thumbnail string `json:"thumbnail"`
  206. Languages []string `json:"languages"`
  207. Stats Stats `json:"stats"`
  208. Registrations bool `json:"registrations"`
  209. ApprovalRequired bool `json:"approval_required"`
  210. InvitesEnabled bool `json:"invites_enabled"`
  211. }
  212. if !data.GetFederationEnabled() {
  213. w.WriteHeader(http.StatusMethodNotAllowed)
  214. return
  215. }
  216. serverURL := data.GetServerURL()
  217. if serverURL == "" {
  218. w.WriteHeader(http.StatusNotFound)
  219. return
  220. }
  221. thumbnail, err := url.Parse(serverURL)
  222. if err != nil {
  223. w.WriteHeader(http.StatusInternalServerError)
  224. return
  225. }
  226. thumbnail.Path = "/logo/external"
  227. localPostCount, _ := persistence.GetLocalPostCount()
  228. res := response{
  229. URI: serverURL,
  230. Title: data.GetServerName(),
  231. ShortDescription: data.GetServerSummary(),
  232. Description: data.GetServerSummary(),
  233. Version: config.GetReleaseString(),
  234. Stats: Stats{
  235. UserCount: 1,
  236. StatusCount: int(localPostCount),
  237. DomainCount: 0,
  238. },
  239. Thumbnail: thumbnail.String(),
  240. Registrations: false,
  241. ApprovalRequired: false,
  242. InvitesEnabled: false,
  243. }
  244. if err := writeResponse(res, w); err != nil {
  245. log.Errorln(err)
  246. }
  247. }
  248. func writeResponse(payload interface{}, w http.ResponseWriter) error {
  249. accountName := data.GetDefaultFederationUsername()
  250. actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
  251. publicKey := crypto.GetPublicKey(actorIRI)
  252. return requests.WritePayloadResponse(payload, w, publicKey)
  253. }
  254. // HostMetaController points to webfinger.
  255. func HostMetaController(w http.ResponseWriter, r *http.Request) {
  256. serverURL := data.GetServerURL()
  257. if serverURL == "" {
  258. w.WriteHeader(http.StatusNotFound)
  259. return
  260. }
  261. res := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
  262. <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  263. <Link rel="lrdd" type="application/json" template="%s/.well-known/webfinger?resource={uri}"/>
  264. </XRD>`, serverURL)
  265. if _, err := w.Write([]byte(res)); err != nil {
  266. log.Errorln(err)
  267. }
  268. }