diff --git a/.generated b/.generated new file mode 100644 index 00000000..e69de29b diff --git a/Makefile b/Makefile index c3a90140..d28af3bc 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,97 @@ -SOURCE := $(shell find . -name "*.go") +## +## functions +## +rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) -install: - go install ./cmd/calc-www +## +## vars +## +GOPATH ?= $(HOME)/go +BIN = $(GOPATH)/bin/calcbiz +SOURCES = $(call rwildcard, ./, *.go) +OUR_SOURCES = $(filter-out $(call rwildcard,vendor//,*.go),$(SOURCES)) +PROTOS = $(call rwildcard, ./, *.proto) +OUR_PROTOS = $(filter-out $(call rwildcard,vendor//,*.proto),$(PROTOS)) +GENERATED_FILES = \ + $(patsubst %.proto,%.pb.go,$(PROTOS)) \ + $(call rwildcard ./, *.gen.go) +PROTOC_OPTS = -I/protobuf:vendor:. +RUN_OPTS ?= -run: up +## +## rules +## -up: - docker-compose up -d --force-recreate --remove-orphans +.PHONY: help +help: + @echo "Make commands:" + @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: \ + '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | \ + sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | grep -v / | \ + sed 's/^/ $(HELP_MSG_PREFIX)make /' -.PHONY: dev -dev: install - calc-www server +.PHONY: run +run: $(BIN) + $(BIN) server $(RUN_OPTS) +.PHONY: install +install: $(BIN) +$(BIN): .generated $(OUR_SOURCES) + go install -v . -.PHONY: docker -docker: - docker build -t ultreme/calcbiz . +.PHONY: clean +clean: + rm -f $(GENERATED_FILES) .generated + +.PHONY: _ci_prepare +_ci_prepare: + touch $(OUR_PROTOS) $(GENERATED_FILES) + sleep 1 + touch .generated +.PHONY: generate +generate: .generated +.generated: $(OUR_PROTOS) + rm -f $(GENERATED_FILES) + go mod vendor + docker run \ + --user="$(shell id -u)" \ + --volume="$(PWD):/go/src/ultre.me/calcbiz" \ + --workdir="/go/src/ultre.me/calcbiz" \ + --entrypoint="sh" \ + --rm \ + pathwar/protoc:v1 \ + -xec "make _generate" + touch $@ + +.PHONY: _generate +_generate: $(GENERATED_FILES) .PHONY: test -test: +test: .generated go test -v ./... + +%.pb.go: %.proto + protoc \ + $(PROTOC_OPTS) \ + --grpc-gateway_out=logtostderr=true:"$(GOPATH)/src" \ + --gogofaster_out=plugins=grpc:"$(GOPATH)/src" \ + "$(dir $<)"/*.proto + +.PHONY: lint +lint: + golangci-lint run --verbose ./... + +## +## production +## + +.PHONY: docker.up +docker.up: + docker-compose up -d --force-recreate --remove-orphans + +.PHONY: docker.build +docker.build: + docker build -t ultreme/calcbiz . diff --git a/api/api.proto b/api/api.proto new file mode 100644 index 00000000..41d58f79 --- /dev/null +++ b/api/api.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; + +package calcbiz.server; + +import "google/api/annotations.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +//import "google/protobuf/timestamp.proto"; + +option go_package = "calcbiz.pw/server"; +option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +service Server { + rpc KryptosEncrypt(KryptosInput) returns (KryptosOutput) { + option (google.api.http) = { + post: "/kryptos/encrypt" + body: "*" + }; + }; + rpc KryptosDecrypt(KryptosInput) returns (KryptosOutput) { + option (google.api.http) = { + post: "/kryptos/encrypt" + body: "*" + }; + }; + rpc TpyoEnocde(TpyoEnocdeIpunt) returns (TpyoEnocdeOuptut) { + option (google.api.http) = { + post: "/spreadshirt/all" + body: "*" + }; } + + rpc Ping(Void) returns (Void) { option (google.api.http) = { get: "/ping" }; }; + rpc Dashboard(Void) returns (DashboardOutput) { option (google.api.http) = {get: "/dashboard"}; } + rpc Crew(Void) returns (CrewOutput) { option (google.api.http) = {get: "/crew"}; } + rpc Numberinfo(NumberinfoInput) returns (NumberinfoOutput) { option (google.api.http) = {get: "/numberinfo/{number}"}; } + rpc Recettator(RecettatorInput) returns (RecettatorOutput) { option (google.api.http) = {get: "/recettator/{seed}"}; } + rpc Moijaime(Void) returns (MoijaimeOutput) { option (google.api.http) = {get: "/moijaime"}; } + rpc SpreadshirtRandom(Void) returns (SpreadshirtRandomOutput) { option (google.api.http) = {get: "/spreadshirt/random"}; } + rpc SpreadshirtAll(Void) returns (SpreadshirtAllOutput) { option (google.api.http) = {get: "/spreadshirt/all"}; } + rpc Wotd(Void) returns (WotdOutput) { option (google.api.http) = {get: "/wotd"}; } + rpc AlternateLogo(Void) returns (AlternateLogoOutput) { option (google.api.http) = {get: "/alternate-logo"}; } + rpc SoundcloudMe(Void) returns (SoundcloudMeOutput) { option (google.api.http) = {get: "/soundcloud/me"}; } + rpc SoundcloudPlaylists(Void) returns (SoundcloudPlaylistsOutput) { option (google.api.http) = {get: "/soundcloud/playlists"}; } + rpc SoundcloudPlaylist(SoundcloudPlaylistInput) returns (SoundcloudPlaylistOutput) { option (google.api.http) = {get: "/soundcloud/playlists/{playlist_id}"}; } + rpc SoundcloudTracks(Void) returns (SoundcloudTracksOutput) { option (google.api.http) = {get: "/soundcloud/tracks"}; } + rpc SoundcloudTrack(SoundcloudTrackInput) returns (SoundcloudTrackOutput) { option (google.api.http) = {get: "/soundcloud/tracks/{track_id}"}; } + + // SoundcloudAlbums + // Airtable... + // Amuse... + // Distrokid... + // Apple... + // Musicbrainz... + // Genius... +} + +message Void {} + +message CrewOutput { /* TODO */ } +message KryptosInput { string from = 1; } +message KryptosOutput { string to = 1; } +message TpyoEnocdeIpunt { string from = 1; } +message TpyoEnocdeOuptut { string to = 1; } +message DahsboardRandomOutput { /* TODO */ } +message NumberinfoInput { string number = 1; } +message NumberinfoOutput { /* TODO */ } +message MoijaimeOutput { repeated string kiffs = 1; } +message WotdOutput { string word = 1; } +message AlternateLogoOutput { string path = 1; } +message DahsboardOutput { /* TODO */ } +message RecettatorInput { int64 seed = 1; } +message RecettatorOutput { /* TODO */ } +message DashboardOutput { /* TODO */ } +message SpreadshirtRandomOutput { /* TODO */ } +message SpreadshirtAllOutput { /* TODO */ } +message SoundcloudMeOutput { /* TODO */ } +message SoundcloudPlaylistsOutput { /* TODO */ } +message SoundcloudPlaylistOutput { /* TODO */ } +message SoundcloudTracksOutput { /* TODO */ } +message SoundcloudTrackOutput { /* TODO */ } +message SoundcloudPlaylistInput { string playlist_id = 1; } +message SoundcloudTrackInput { string track_id = 1; } diff --git a/calc.go b/calc.go deleted file mode 100644 index a5f90f2d..00000000 --- a/calc.go +++ /dev/null @@ -1,4 +0,0 @@ -package calc // import "ultre.me/calcbiz" - -// VERSION represents the version of the Camembert au lait crew's website -const VERSION = "2.1.0" diff --git a/cmd/calc-www/main.go b/cmd/calc-www/main.go deleted file mode 100644 index 860994e4..00000000 --- a/cmd/calc-www/main.go +++ /dev/null @@ -1,354 +0,0 @@ -package main - -import ( - "net/http" - "os" - "time" - - "github.com/gin-gonic/gin" - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/go-chi/render" - "github.com/gobuffalo/packr" - "github.com/urfave/cli" - - "ultre.me/calcbiz" - "ultre.me/calcbiz/pkg/dashboard" - "ultre.me/calcbiz/pkg/log" - "ultre.me/calcbiz/pkg/soundcloud" -) - -func main() { - app := cli.NewApp() - app.Name = "calc-www" - app.Usage = "Camembert au lait crew's web server" - app.Version = calc.VERSION - - app.Flags = []cli.Flag{ - cli.BoolFlag{ - Name: "debug, D", - Usage: "Enable debug mode", - }, - } - - app.Before = func(c *cli.Context) error { - if c.Bool("debug") { - log.SetDebug(true) - } - return nil - } - - app.Commands = []cli.Command{ - { - Name: "server", - Usage: "Start a calc-www server", - Action: server, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "bind-address, b", - Usage: "TCP port address", - Value: ":9000", - }, - cli.StringFlag{ - Name: "soundcloud-client-id", - Value: "", - Usage: "SoundCloud CLIENT_ID", - EnvVar: "SOUNDCLOUD_CLIENT_ID", - }, - cli.IntFlag{ - Name: "soundcloud-user-id", - Value: 96137699, - Usage: "SoundCloud USER_ID", - EnvVar: "SOUNDCLOUD_USER_ID", - }, - }, - }, - } - - if err := app.Run(os.Args); err != nil { - log.Fatalf("%v", err) - } -} - -func server(c *cli.Context) error { - r := chi.NewRouter() - //r.Use(middleware.RequestID) - //r.Use(middleware.RealIP) - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - //r.Use(middleware.URLFormat) - r.Use(middleware.Timeout(5 * time.Second)) - - r.Route("/api", func(r chi.Router) { - r.Use(render.SetContentType(render.ContentTypeJSON)) - - // ping - pong := func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`{"result":"pong"}`)) - } - r.Get("/ping", pong) - r.Post("/ping", pong) - r.Options("/ping", pong) - r.Trace("/ping", pong) - r.Connect("/ping", pong) - r.Head("/ping", pong) - r.Put("/ping", pong) - r.Patch("/ping", pong) - r.Delete("/ping", pong) - - soundcloud := calcsoundcloud.New(c.String("soundcloud-client-id"), uint64(c.Int("soundcloud-user-id"))) - dashboard := calcdashboard.New() - dashboard.SetSoundCloud(&soundcloud) - - // dashboard - r.Get("/dashboard/random", func(w http.ResponseWriter, r *http.Request) { - dashboard, err := dashboard.Random() - if err != nil { - c.JSON(http.StatusNotFound, gin.H{ - "error": err, - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": dashboard, - }) - } - }) - - /* - // crew - r.Get("/crew", func(w http.ResponseWriter, r *http.Request) { - c.JSON(http.StatusOK, gin.H{ - "result": calccrew.CALC, - }) - }) - - // numberinfo - r.Get("/numberinfo/all/:number", func(w http.ResponseWriter, r *http.Request) { - number, err := strconv.ParseFloat(c.Param("number"), 64) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid number: %v (%v)", c.Param("number"), err), - }) - return - } - - info := calcnumberinfo.New(number).All() - c.JSON(http.StatusOK, gin.H{ - "result": info, - }) - }) - - // recettator - r.Get("/recettator/json/:seed", func(w http.ResponseWriter, r *http.Request) { - seed, err := strconv.ParseInt(c.Param("seed"), 10, 64) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid seed: %v (%v)", c.Param("seed"), err), - }) - return - } - - rctt := recettator.New(seed) - rctt.SetSettings(recettator.Settings{ - MainIngredients: 2, - SecondaryIngredients: 2, - Steps: 5, - }) - - output := rctt.ToMap() - - c.JSON(http.StatusOK, gin.H{ - "result": output, - }) - }) - - // moijaime - r.Get("/moijaime", func(w http.ResponseWriter, r *http.Request) { - phrases := []string{} - for i := 0; i < 20; i++ { - phrases = append(phrases, moijaime.Generate()) - } - c.JSON(http.StatusOK, gin.H{ - "result": phrases, - }) - }) - - // spreadshirt - r.Get("/spreadshirt/random", func(w http.ResponseWriter, r *http.Request) { - c.JSON(http.StatusOK, gin.H{ - "result": calcspreadshirt.GetRandomProduct(250, 250), - }) - }) - r.Get("/spreadshirt/all", func(w http.ResponseWriter, r *http.Request) { - c.JSON(http.StatusOK, gin.H{ - "result": calcspreadshirt.GetAllProducts(250, 250), - }) - }) - - // kryptos - r.Post("/kryptos/encrypt", func(w http.ResponseWriter, r *http.Request) { - var data struct { - Message string - } - if err := c.BindJSON(&data); err == nil { - c.JSON(http.StatusOK, gin.H{ - "result": kryptos.Encrypt(data.Message), - }) - } else { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid input: %v", err), - }) - } - }) - r.Post("/kryptos/decrypt", func(w http.ResponseWriter, r *http.Request) { - var data struct { - Message string - } - if err := c.BindJSON(&data); err == nil { - c.JSON(http.StatusOK, gin.H{ - "result": kryptos.Decrypt(data.Message), - }) - } else { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid input: %v", err), - }) - } - }) - - // tpyo - r.Post("/tpyo/enocde", func(w http.ResponseWriter, r *http.Request) { - var data struct { - Message string - } - if err := c.BindJSON(&data); err == nil { - enedocr := tpyo.NewTpyo() - c.JSON(http.StatusOK, gin.H{ - "result": enedocr.Enocde(data.Message), - }) - } else { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid input: %v", err), - }) - } - }) - - // random - r.Get("/random/wotd", func(w http.ResponseWriter, r *http.Request) { - c.JSON(http.StatusOK, gin.H{ - "result": calcrand.WOTD(), - }) - }) - r.Get("/random/alternate-logo", func(w http.ResponseWriter, r *http.Request) { - c.JSON(http.StatusOK, gin.H{ - "result": calcrand.AlternateLogo(), - }) - }) - - // soundcloud - r.Get("/soundcloud/me", func(w http.ResponseWriter, r *http.Request) { - me, err := soundcloud.Me() - if err != nil { - log.Warnf("failed to get /api/soundcloud/me: %v", err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": me, - }) - } - }) - r.Get("/soundcloud/playlists", func(w http.ResponseWriter, r *http.Request) { - playlists, err := soundcloud.Playlists() - if err != nil { - log.Warnf("failed to get /api/soundcloud/playlists: %v", err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": playlists, - }) - } - }) - r.Get("/soundcloud/playlists/:id", func(w http.ResponseWriter, r *http.Request) { - playlistID, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid playlist id: %v", c.Param("id")), - }) - return - } - - playlist, err := soundcloud.Playlist(playlistID) - if err != nil { - log.Warnf("failed to get /api/soundcloud/playlists/%d: %v", playlistID, err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": playlist, - }) - } - }) - r.Get("/soundcloud/tracks/:id", func(w http.ResponseWriter, r *http.Request) { - if c.Param("id") == "random" { - track, err := soundcloud.RandomTrack() - if err != nil { - log.Warnf("failed to get /api/soundcloud/tracks/random: %v", err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": track, - }) - } - return - } - trackID, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{ - "error": fmt.Sprintf("Invalid track id: %v", c.Param("id")), - }) - return - } - - track, err := soundcloud.Track(trackID) - if err != nil { - log.Warnf("failed to get /api/soundcloud/tracks/%d: %v", trackID, err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": track, - }) - } - }) - r.Get("/soundcloud/tracks", func(w http.ResponseWriter, r *http.Request) { - tracks, err := soundcloud.Tracks() - if err != nil { - log.Warnf("failed to get /api/soundcloud/tracks: %v", err) - c.JSON(http.StatusNotFound, gin.H{ - "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), - }) - } else { - c.JSON(http.StatusOK, gin.H{ - "result": tracks, - }) - } - }) - */ - }) - - // static files - box := packr.NewBox("./static") - r.Handle("/", http.FileServer(box)) - - // FIXME: handle socket.io - http.Handle("/", r) - log.Debugf("Listening and serving HTTP on %s", c.String("bind-address")) - return http.ListenAndServe(c.String("bind-address"), nil) -} diff --git a/go.mod b/go.mod index 6f2bf87f..ef23717a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module ultre.me/calcbiz require ( github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect - github.com/gin-gonic/gin v1.3.0 + github.com/gin-gonic/gin v1.3.0 // indirect github.com/go-chi/chi v3.3.3+incompatible github.com/go-chi/render v1.0.1 github.com/gobuffalo/packr v1.13.7 @@ -18,7 +18,7 @@ require ( github.com/shazow/memoizer v0.0.0-20130904030615-74fc48eaeadc github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect - github.com/tpyolang/tpyo-cli v0.0.0-20160926074508-6ba04c3a8dec + github.com/tpyolang/tpyo-cli v0.0.0-20160926074508-6ba04c3a8dec // indirect github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f // indirect github.com/urfave/cli v1.20.0 github.com/yanatan16/golang-soundcloud v0.0.0-20161013200625-a182dd459b66 @@ -26,7 +26,7 @@ require ( golang.org/x/sys v0.0.0-20181023152157-44b849a8bc13 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 // indirect - ultre.me/kryptos v0.0.0-20181023194748-240fe1a16033 - ultre.me/moi-j-aime-generator v0.0.0-20181023194533-7b56f5b329e1 - ultre.me/recettator v0.4.0 + ultre.me/kryptos v0.0.0-20181023194748-240fe1a16033 // indirect + ultre.me/moi-j-aime-generator v0.0.0-20181023194533-7b56f5b329e1 // indirect + ultre.me/recettator v0.4.0 // indirect ) diff --git a/main.go b/main.go new file mode 100644 index 00000000..d3cfcf5f --- /dev/null +++ b/main.go @@ -0,0 +1,355 @@ +package main // import "ultre.me/calcbiz" + +import ( + "net/http" + "os" + "time" + + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/go-chi/render" + "github.com/gobuffalo/packr" + "github.com/urfave/cli" + + "ultre.me/calcbiz/pkg/dashboard" + "ultre.me/calcbiz/pkg/log" + "ultre.me/calcbiz/pkg/soundcloud" +) + +// VERSION represents the version of the Camembert au lait crew's website +const VERSION = "2.1.0" + +func main() { + app := cli.NewApp() + app.Name = "calcbiz" + app.Usage = "Camembert au lait crew's web server" + app.Version = VERSION + + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug, D", + Usage: "Enable debug mode", + }, + } + + app.Before = func(c *cli.Context) error { + if c.Bool("debug") { + log.SetDebug(true) + } + return nil + } + + app.Commands = []cli.Command{ + { + Name: "server", + Usage: "Start a calc-www server", + Action: server, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "bind-address, b", + Usage: "TCP port address", + Value: ":9000", + }, + cli.StringFlag{ + Name: "soundcloud-client-id", + Value: "", + Usage: "SoundCloud CLIENT_ID", + EnvVar: "SOUNDCLOUD_CLIENT_ID", + }, + cli.IntFlag{ + Name: "soundcloud-user-id", + Value: 96137699, + Usage: "SoundCloud USER_ID", + EnvVar: "SOUNDCLOUD_USER_ID", + }, + }, + }, + } + + if err := app.Run(os.Args); err != nil { + log.Fatalf("%v", err) + } +} + +func server(c *cli.Context) error { + r := chi.NewRouter() + //r.Use(middleware.RequestID) + //r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + //r.Use(middleware.URLFormat) + r.Use(middleware.Timeout(5 * time.Second)) + + r.Route("/api", func(r chi.Router) { + r.Use(render.SetContentType(render.ContentTypeJSON)) + + // ping + pong := func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"result":"pong"}`)) + } + r.Get("/ping", pong) + r.Post("/ping", pong) + r.Options("/ping", pong) + r.Trace("/ping", pong) + r.Connect("/ping", pong) + r.Head("/ping", pong) + r.Put("/ping", pong) + r.Patch("/ping", pong) + r.Delete("/ping", pong) + + soundcloud := calcsoundcloud.New(c.String("soundcloud-client-id"), uint64(c.Int("soundcloud-user-id"))) + dashboard := calcdashboard.New() + dashboard.SetSoundCloud(&soundcloud) + + /* + // dashboard + r.Get("/dashboard/random", func(w http.ResponseWriter, r *http.Request) { + dashboard, err := dashboard.Random() + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": err, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": dashboard, + }) + } + }) + + // crew + r.Get("/crew", func(w http.ResponseWriter, r *http.Request) { + c.JSON(http.StatusOK, gin.H{ + "result": calccrew.CALC, + }) + }) + + // numberinfo + r.Get("/numberinfo/all/:number", func(w http.ResponseWriter, r *http.Request) { + number, err := strconv.ParseFloat(c.Param("number"), 64) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid number: %v (%v)", c.Param("number"), err), + }) + return + } + + info := calcnumberinfo.New(number).All() + c.JSON(http.StatusOK, gin.H{ + "result": info, + }) + }) + + // recettator + r.Get("/recettator/json/:seed", func(w http.ResponseWriter, r *http.Request) { + seed, err := strconv.ParseInt(c.Param("seed"), 10, 64) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid seed: %v (%v)", c.Param("seed"), err), + }) + return + } + + rctt := recettator.New(seed) + rctt.SetSettings(recettator.Settings{ + MainIngredients: 2, + SecondaryIngredients: 2, + Steps: 5, + }) + + output := rctt.ToMap() + + c.JSON(http.StatusOK, gin.H{ + "result": output, + }) + }) + + // moijaime + r.Get("/moijaime", func(w http.ResponseWriter, r *http.Request) { + phrases := []string{} + for i := 0; i < 20; i++ { + phrases = append(phrases, moijaime.Generate()) + } + c.JSON(http.StatusOK, gin.H{ + "result": phrases, + }) + }) + + // spreadshirt + r.Get("/spreadshirt/random", func(w http.ResponseWriter, r *http.Request) { + c.JSON(http.StatusOK, gin.H{ + "result": calcspreadshirt.GetRandomProduct(250, 250), + }) + }) + r.Get("/spreadshirt/all", func(w http.ResponseWriter, r *http.Request) { + c.JSON(http.StatusOK, gin.H{ + "result": calcspreadshirt.GetAllProducts(250, 250), + }) + }) + + // kryptos + r.Post("/kryptos/encrypt", func(w http.ResponseWriter, r *http.Request) { + var data struct { + Message string + } + if err := c.BindJSON(&data); err == nil { + c.JSON(http.StatusOK, gin.H{ + "result": kryptos.Encrypt(data.Message), + }) + } else { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid input: %v", err), + }) + } + }) + r.Post("/kryptos/decrypt", func(w http.ResponseWriter, r *http.Request) { + var data struct { + Message string + } + if err := c.BindJSON(&data); err == nil { + c.JSON(http.StatusOK, gin.H{ + "result": kryptos.Decrypt(data.Message), + }) + } else { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid input: %v", err), + }) + } + }) + + // tpyo + r.Post("/tpyo/enocde", func(w http.ResponseWriter, r *http.Request) { + var data struct { + Message string + } + if err := c.BindJSON(&data); err == nil { + enedocr := tpyo.NewTpyo() + c.JSON(http.StatusOK, gin.H{ + "result": enedocr.Enocde(data.Message), + }) + } else { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid input: %v", err), + }) + } + }) + + // random + r.Get("/random/wotd", func(w http.ResponseWriter, r *http.Request) { + c.JSON(http.StatusOK, gin.H{ + "result": calcrand.WOTD(), + }) + }) + r.Get("/random/alternate-logo", func(w http.ResponseWriter, r *http.Request) { + c.JSON(http.StatusOK, gin.H{ + "result": calcrand.AlternateLogo(), + }) + }) + + // soundcloud + r.Get("/soundcloud/me", func(w http.ResponseWriter, r *http.Request) { + me, err := soundcloud.Me() + if err != nil { + log.Warnf("failed to get /api/soundcloud/me: %v", err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": me, + }) + } + }) + r.Get("/soundcloud/playlists", func(w http.ResponseWriter, r *http.Request) { + playlists, err := soundcloud.Playlists() + if err != nil { + log.Warnf("failed to get /api/soundcloud/playlists: %v", err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": playlists, + }) + } + }) + r.Get("/soundcloud/playlists/:id", func(w http.ResponseWriter, r *http.Request) { + playlistID, err := strconv.ParseUint(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid playlist id: %v", c.Param("id")), + }) + return + } + + playlist, err := soundcloud.Playlist(playlistID) + if err != nil { + log.Warnf("failed to get /api/soundcloud/playlists/%d: %v", playlistID, err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": playlist, + }) + } + }) + r.Get("/soundcloud/tracks/:id", func(w http.ResponseWriter, r *http.Request) { + if c.Param("id") == "random" { + track, err := soundcloud.RandomTrack() + if err != nil { + log.Warnf("failed to get /api/soundcloud/tracks/random: %v", err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": track, + }) + } + return + } + trackID, err := strconv.ParseUint(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": fmt.Sprintf("Invalid track id: %v", c.Param("id")), + }) + return + } + + track, err := soundcloud.Track(trackID) + if err != nil { + log.Warnf("failed to get /api/soundcloud/tracks/%d: %v", trackID, err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": track, + }) + } + }) + r.Get("/soundcloud/tracks", func(w http.ResponseWriter, r *http.Request) { + tracks, err := soundcloud.Tracks() + if err != nil { + log.Warnf("failed to get /api/soundcloud/tracks: %v", err) + c.JSON(http.StatusNotFound, gin.H{ + "error": soundcloud.EscapeString(fmt.Sprintf("%v", err)), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "result": tracks, + }) + } + }) + */ + }) + + // static files + box := packr.NewBox("./static") + r.Handle("/", http.FileServer(box)) + + // FIXME: handle socket.io + http.Handle("/", r) + log.Infof("Listening and serving HTTP on %s", c.String("bind-address")) + return http.ListenAndServe(c.String("bind-address"), nil) +}