Compare commits

..

3 Commits

Author SHA1 Message Date
Martyn 68df7a5af2 Merge pull request 'react-frontend' (#2) from react-frontend into main
continuous-integration/drone/push Build is failing Details
Reviewed-on: #2
2020-08-01 09:40:05 +00:00
Martyn 4c27138497 Build the React frontend
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-08-01 11:38:05 +02:00
Martyn 8de2c0bd74 Serve the React frontend
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-08-01 11:37:40 +02:00
9 changed files with 101 additions and 7 deletions

3
.gitignore vendored
View File

@ -12,4 +12,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Frontend compiled version gets built from source
/web/react-frontend
/twitchsingstools /twitchsingstools

View File

@ -10,6 +10,11 @@ test:
build: build:
go build ${LDFLAGS} go build ${LDFLAGS}
build-frontend:
cd build/react-frontend && npm run build
rm -rf web/react-frontend ; mkdir -p web/react-frontend
cp -r build/react-frontend/build/* web/react-frontend/
deps: deps:
go get go get

View File

@ -59,6 +59,11 @@ steps:
- make test - make test
- make - make
- name: build-frontend
image: node
commands:
- make frontend
trigger: trigger:
ref: ref:
- refs/heads/main - refs/heads/main

View File

@ -5,9 +5,14 @@ COPY internal/ /go/src/git.martyn.berlin/martyn/twitchsingstools/internal/
COPY Makefile /go/src/git.martyn.berlin/martyn/twitchsingstools/ COPY Makefile /go/src/git.martyn.berlin/martyn/twitchsingstools/
RUN cd /go/src/git.martyn.berlin/martyn/twitchsingstools/; make deps ; make static RUN cd /go/src/git.martyn.berlin/martyn/twitchsingstools/; make deps ; make static
FROM scratch FROM library/node:14.7.0-stretch AS frontend
COPY build/react-frontend /frontend
RUN cd /frontend; npm run build
FROM debian
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /go/src/git.martyn.berlin/martyn/twitchsingstools /app/ COPY --from=builder /go/src/git.martyn.berlin/martyn/twitchsingstools /app/
COPY web/ /app/web/ COPY web/ /app/web/
COPY --from=frontend /frontend/build /app/web/react-frontend
WORKDIR /app WORKDIR /app
CMD ["/app/twitchsingstools"] CMD ["/app/twitchsingstools"]

1
build/react-frontend Submodule

@ -0,0 +1 @@
Subproject commit 5d2ed3c5be7d050e48b6bedbd2591543d732bf52

View File

@ -778,23 +778,80 @@ func CSVHandler(response http.ResponseWriter, request *http.Request) {
} }
} }
func JSONHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
if vars["key"] != ircBot.ChannelData[vars["channel"]].AdminKey {
UnauthorizedHandler(response, request)
return
}
type TemplateData struct {
Channel string
Command string
ExtraStrings string
SinceTime time.Time
SinceTimeUTC string
Leaving bool
HasLeft bool
SongData []AugmentedSingsVideoStruct
TopNSongs []SongSings
TopNSingers []SingerSings
}
channelData := ircBot.ChannelData[vars["channel"]]
if time.Now().Sub(ircBot.ChannelData[vars["channel"]].VideoCacheUpdated).Hours() > 1 {
fmt.Printf("Cache of %d performances is older than an hour - %.1f hours old to be precise... fetching.\n", len(ircBot.ChannelData[vars["channel"]].VideoCache), time.Now().Sub(ircBot.ChannelData[vars["channel"]].VideoCacheUpdated).Hours())
vids, err := fetchAllVoDs(channelData.TwitchUserID, channelData.Bearer)
if err != nil {
errCache := make([]irc.SingsVideoStruct, 0)
var ret irc.SingsVideoStruct
ret.FullTitle = "Error fetching videos: " + err.Error()
errCache = append(errCache, ret)
vids = errCache
}
updateCalculatedFields(vids)
ircBot.UpdateVideoCache(vars["channel"], vids)
} else {
fmt.Printf("Cache of %d performances is younger than an hour - %.1f hours old to be precise... not fetching.\n", len(ircBot.ChannelData[vars["channel"]].VideoCache), time.Now().Sub(ircBot.ChannelData[vars["channel"]].VideoCacheUpdated).Hours())
}
topNSongs := calculateTopNSongs(channelData.VideoCache, 10)
topNSingers := calculateTopNSingers(channelData.VideoCache, 10)
var td = TemplateData{channelData.Name, channelData.Command, channelData.ExtraStrings, channelData.JoinTime, channelData.JoinTime.Format(irc.UTCFormat), false, channelData.HasLeft, AugmentSingsVideoStructSlice(channelData.VideoCache), topNSongs, topNSingers}
response.Header().Add("Content-type", "application/json")
if request.URL.Path[0:5] == "/json" {
tmpl := template.Must(template.ParseFiles("web/data.json"))
tmpl.Execute(response, td)
} else if request.URL.Path[0:9] == "/topsongs" {
tmpl := template.Must(template.ParseFiles("web/topsongs.json"))
tmpl.Execute(response, td)
} else { // top 10 singers!
tmpl := template.Must(template.ParseFiles("web/topsingers.json"))
tmpl.Execute(response, td)
}
}
func ReactIndexHandler(entrypoint string) func(w http.ResponseWriter, r *http.Request) {
fn := func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, entrypoint)
}
return http.HandlerFunc(fn)
}
func HandleHTTP(passedIrcBot *irc.KardBot) { func HandleHTTP(passedIrcBot *irc.KardBot) {
ircBot = passedIrcBot ircBot = passedIrcBot
r := mux.NewRouter() r := mux.NewRouter()
loggedRouter := handlers.LoggingHandler(os.Stdout, r) loggedRouter := handlers.LoggingHandler(os.Stdout, r)
r.NotFoundHandler = http.HandlerFunc(NotFoundHandler) r.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
r.HandleFunc("/", RootHandler)
r.HandleFunc("/healthz", HealthHandler) r.HandleFunc("/healthz", HealthHandler)
r.HandleFunc("/web/{.*}", TemplateHandler) r.HandleFunc("/web/{.*}", TemplateHandler)
r.PathPrefix("/static/").Handler(http.FileServer(http.Dir("./web/")))
r.HandleFunc("/cover.css", CSSHandler)
r.HandleFunc("/admin/{channel}/{key}", AdminHandler)
r.HandleFunc("/csv/{channel}/{key}", CSVHandler) r.HandleFunc("/csv/{channel}/{key}", CSVHandler)
r.HandleFunc("/tsv/{channel}/{key}", CSVHandler) r.HandleFunc("/tsv/{channel}/{key}", CSVHandler)
//r.HandleFunc("/twitchadmin", TwitchAdminHandler) r.HandleFunc("/json/{channel}/{key}", JSONHandler)
//r.HandleFunc("/twitchtobackend", TwitchBackendHandler) r.HandleFunc("/topsongs/{channel}/{key}", JSONHandler)
r.HandleFunc("/topsingers/{channel}/{key}", JSONHandler)
r.Path("/twitchtobackend").Queries("access_token", "{access_token}", "scope", "{scope}", "token_type", "{token_type}").HandlerFunc(TwitchBackendHandler) r.Path("/twitchtobackend").Queries("access_token", "{access_token}", "scope", "{scope}", "token_type", "{token_type}").HandlerFunc(TwitchBackendHandler)
r.Path("/twitchadmin").Queries("code", "{code}", "scope", "{scope}").HandlerFunc(TwitchAdminHandler) r.Path("/twitchadmin").Queries("code", "{code}", "scope", "{scope}").HandlerFunc(TwitchAdminHandler)
r.PathPrefix("/static").Handler(http.StripPrefix("/static", http.FileServer(http.Dir("./web/react-frontend/static"))))
r.PathPrefix("/").HandlerFunc(ReactIndexHandler("./web/react-frontend/index.html"))
http.Handle("/", r) http.Handle("/", r)
srv := &http.Server{ srv := &http.Server{
Handler: loggedRouter, Handler: loggedRouter,

10
web/data.json Executable file
View File

@ -0,0 +1,10 @@
[{{ range $index, $data := .SongData }}{{ if $index }},{{end}}{
"publishDate": "{{ $data.Date }}",
"displayPublishDate": "{{ $data.NiceDate }}",
"songName": "{{ $data.SongTitle }}",
"singerName": "{{ $data.OtherSinger }}",
"lastSongDate": "{{ $data.LastSungSong }}",
"displayLastSongDate": "{{ $data.NiceLastSungSong }}",
"lastDuetDate": "{{ $data.LastSungSinger }}",
"displayLastDuetDate": "{{ $data.NiceLastSungSinger }}"
}{{ end }}]

4
web/topsingers.json Executable file
View File

@ -0,0 +1,4 @@
[{{ range $index, $data := .TopNSingers }}{{ if $index }},{{end}}{
"singerName": "{{ .SingerName }}",
"singCount": "{{ .Sings }}"
}{{ end }}]

4
web/topsongs.json Executable file
View File

@ -0,0 +1,4 @@
[{{ range $index, $data := .TopNSongs }}{{ if $index }},{{end}}{
"songName": "{{ .SongTitle }}",
"singCount": "{{ .Sings }}"
}{{ end }}]