fork
Configure Feed
Select the types of activity you want to include in your feed.
Live video on the AT Protocol
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package api
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "strconv"
9 "strings"
10
11 "github.com/julienschmidt/httprouter"
12 "stream.place/streamplace/pkg/aqtime"
13 apierrors "stream.place/streamplace/pkg/errors"
14 "stream.place/streamplace/pkg/log"
15)
16
17const BRANCH = "latest"
18
19func formatRequest(r *http.Request) string {
20 // Create return string
21 var request []string
22 // Add the request string
23 url := fmt.Sprintf("%v %v %v", r.Method, r.URL, r.Proto)
24 request = append(request, url)
25 // Add the host
26 request = append(request, fmt.Sprintf("Host: %v", r.Host))
27 // Loop through headers
28 for name, headers := range r.Header {
29 name = strings.ToLower(name)
30 for _, h := range headers {
31 request = append(request, fmt.Sprintf("%v: %v", name, h))
32 }
33 }
34
35 // If this is a POST, add post data
36 if r.Method == "POST" {
37 r.ParseForm()
38 request = append(request, "\n")
39 request = append(request, r.Form.Encode())
40 }
41 // Return the request as a string
42 return strings.Join(request, "\n")
43}
44
45type MacManifestUpdateTo struct {
46 Version string `json:"version"`
47 PubDate string `json:"pub_date"`
48 Notes string `json:"notes"`
49 Name string `json:"name"`
50 URL string `json:"url"`
51}
52
53type MacManifestRelease struct {
54 Version string `json:"version"`
55 UpdateTo MacManifestUpdateTo `json:"updateTo"`
56}
57
58type MacManifest struct {
59 CurrentRelease string `json:"currentRelease"`
60 Releases []MacManifestRelease `json:"releases"`
61}
62
63func (a *StreamplaceAPI) HandleDesktopUpdates(ctx context.Context) httprouter.Handle {
64 mac := a.HandleMacDesktopUpdates(ctx)
65 win := a.HandleWindowsDesktopUpdates(ctx)
66 return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
67 platform := params.ByName("platform")
68 if platform == "darwin" {
69 mac(w, req, params)
70 } else if platform == "windows" {
71 win(w, req, params)
72 } else {
73 apierrors.WriteHTTPBadRequest(w, fmt.Sprintf("unsupported platform: %s", platform), nil)
74 }
75 }
76}
77
78func (a *StreamplaceAPI) HandleMacDesktopUpdates(ctx context.Context) httprouter.Handle {
79 return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
80 platform := params.ByName("platform")
81 architecture := params.ByName("architecture")
82 clientVersion := params.ByName("version")
83 clientBuildTime := params.ByName("buildTime")
84 file := params.ByName("file")
85 if file != "RELEASES.json" {
86 apierrors.WriteHTTPNotFound(w, fmt.Sprintf("unknown file: %s", file), nil)
87 return
88 }
89 log.Log(ctx, formatRequest(req),
90 "platform", platform,
91 "architecture", architecture,
92 "clientVersion", clientVersion,
93 "clientBuildTime", clientBuildTime,
94 )
95 clientBuildSec, err := strconv.ParseInt(clientBuildTime, 10, 64)
96 if err != nil {
97 apierrors.WriteHTTPBadRequest(w, "build time must be a number", err)
98 return
99 }
100 var mani MacManifest
101 if clientBuildSec >= a.CLI.Build.BuildTime {
102 // client is newer or the same as server
103 mani = MacManifest{
104 CurrentRelease: clientVersion,
105 Releases: []MacManifestRelease{},
106 }
107 } else {
108 // we're newer than the client, tell it to update
109 aqt := aqtime.FromSec(a.CLI.Build.BuildTime)
110 // sigh. but at least it's only for dev versions.
111 serverVersionZ := strings.ReplaceAll(a.CLI.Build.Version, "-", "-z")
112 updateTo := MacManifestUpdateTo{
113 Version: serverVersionZ,
114 PubDate: aqt.String(),
115 Notes: fmt.Sprintf("Streamplace %s", clientVersion),
116 Name: fmt.Sprintf("Streamplace %s", clientVersion),
117 URL: fmt.Sprintf("https://%s/dl/%s/streamplace-desktop-%s-%s.zip", req.Host, BRANCH, platform, architecture),
118 }
119
120 mani = MacManifest{
121 CurrentRelease: serverVersionZ,
122 Releases: []MacManifestRelease{
123 {
124 Version: clientVersion,
125 UpdateTo: updateTo,
126 },
127 // todo: this is straight from their example, but why does this version upgrade to itself...?
128 {
129 Version: serverVersionZ,
130 UpdateTo: updateTo,
131 },
132 },
133 }
134 }
135
136 w.Header().Set("content-type", "application/json")
137 w.WriteHeader(200)
138 bs, err := json.Marshal(mani)
139 if err != nil {
140 log.Log(ctx, "error marshaling mac update manifest", "error", err)
141 }
142 w.Write(bs)
143 }
144}
145
146func (a *StreamplaceAPI) HandleWindowsDesktopUpdates(ctx context.Context) httprouter.Handle {
147 return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
148 platform := params.ByName("platform")
149 architecture := params.ByName("architecture")
150 clientVersion := params.ByName("version")
151 clientBuildTime := params.ByName("buildTime")
152 file := params.ByName("file")
153 log.Log(ctx, formatRequest(req),
154 "platform", platform,
155 "architecture", architecture,
156 "clientVersion", clientVersion,
157 "clientBuildTime", clientBuildTime,
158 )
159
160 clientBuildSec, err := strconv.ParseInt(clientBuildTime, 10, 64)
161 if err != nil {
162 apierrors.WriteHTTPBadRequest(w, "build time must be a number", err)
163 return
164 }
165
166 files, err := a.getGitlabPackage(BRANCH)
167 if err != nil {
168 apierrors.WriteHTTPInternalServerError(w, "could not find gitlab package", err)
169 return
170 }
171
172 var gitlabFile *GitlabFile
173 for _, f := range files {
174 if f.Extension == "nupkg" {
175 gitlabFile = &f
176 break
177 }
178 }
179 if gitlabFile == nil {
180 apierrors.WriteHTTPInternalServerError(w, "could not find gitlab package", err)
181 return
182 }
183
184 if file == "RELEASES" {
185 if clientBuildSec >= a.CLI.Build.BuildTime {
186 // client is newer or the same as server
187 fmt.Fprintf(w, "0000000000000000000000000000000000000000 streamplace_desktop-%s-full.nupkg 1", clientVersion)
188 return
189 }
190 fmt.Fprintf(w, "%s streamplace_desktop-%s-full.nupkg %d", gitlabFile.SHA1, gitlabFile.Version, gitlabFile.Size)
191 return
192 }
193 http.Redirect(w, req, gitlabFile.URL(), http.StatusTemporaryRedirect)
194 }
195}