tons of backend changes
This commit is contained in:
parent
996bfea07c
commit
fd536075c5
|
|
@ -1,8 +0,0 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/internal/ws/client.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/internal/ws/handlers.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/internal/ws/hub.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/internal/ws/message.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/internal/ws/structs.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Dockerfile" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cmd/watchtogether/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/watchtogether/main.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/docker-compose.yml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/internal/driver/driver.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/internal/logger/logger.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/internal/server/middleware.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/internal/server/routes.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/internal/server/server.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/services/logger.go" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Go File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="GOROOT" url="file:///usr/lib/go" />
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
||||
</component>
|
||||
<component name="GitSEFilterConfiguration">
|
||||
<file-type-list>
|
||||
<filtered-out-file-type name="LOCAL_BRANCH" />
|
||||
<filtered-out-file-type name="REMOTE_BRANCH" />
|
||||
<filtered-out-file-type name="TAG" />
|
||||
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
|
||||
</file-type-list>
|
||||
</component>
|
||||
<component name="GoLibraries">
|
||||
<option name="indexEntireGoPath" value="false" />
|
||||
</component>
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="ProjectId" id="2456l5ltkI1NptLGqpacBxKAWdj" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="go.format.on.save.advertiser.fired" value="true" />
|
||||
<property name="go.formatter.settings.were.checked" value="true" />
|
||||
<property name="go.import.settings.migrated" value="true" />
|
||||
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
||||
<property name="go.sdk.automatically.set" value="true" />
|
||||
<property name="go.watchers.conflict.with.on.save.actions.check.performed" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="go build github.com/qpixel/watchtogether/cmd/watchtogether" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
|
||||
<module name="backend" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<kind value="PACKAGE" />
|
||||
<package value="github.com/qpixel/watchtogether/cmd/watchtogether" />
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<filePath value="$PROJECT_DIR$/cmd/watchtogether/main.go" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Go Build.go build github.com/qpixel/watchtogether/cmd/watchtogether" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<integration-enabled>true</integration-enabled>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,72 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/peterbourgon/ff/v3"
|
||||
"github.com/qpixel/watchtogether/internal/logger"
|
||||
"github.com/qpixel/watchtogether/internal/server"
|
||||
"github.com/ubergeek77/tinylog"
|
||||
"os"
|
||||
"runtime"
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/qpixel/watchtogether/internal/ws"
|
||||
tlog "github.com/ubergeek77/tinylog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type flags struct {
|
||||
loglvl int
|
||||
port int
|
||||
}
|
||||
|
||||
func newFlags(args []string) (flgs flags, err error) {
|
||||
fs := flag.NewFlagSet(args[0], flag.ContinueOnError)
|
||||
|
||||
var (
|
||||
loglvl = fs.Int("loglvl", tinylog.TraceLevel, "sets the log level (also via LOG_LEVEL)")
|
||||
port = fs.Int("port", 4000, "sets the port to use (also via PORT)")
|
||||
)
|
||||
// Parse the command line flags from above
|
||||
err = ff.Parse(fs, args[1:], ff.WithEnvVarNoPrefix())
|
||||
if err != nil {
|
||||
return flgs, err
|
||||
}
|
||||
return flags{
|
||||
loglvl: *loglvl,
|
||||
port: *port,
|
||||
}, nil
|
||||
}
|
||||
var log = tlog.NewTaggedLogger("Logger", tlog.NewColor("38;5;111"))
|
||||
|
||||
func main() {
|
||||
if err := run(os.Args); err != nil {
|
||||
tinylog.DefaultLogger().Errorf("error from main.run(): %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
resp := make(map[string]string)
|
||||
resp["status"] = "ok"
|
||||
jsonResp, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Fatalf("error in unmarshalling json. err: %s", err.Error())
|
||||
}
|
||||
_, err = w.Write(jsonResp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
})
|
||||
hub := ws.NewHub()
|
||||
go hub.Run()
|
||||
|
||||
}
|
||||
r.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
ws.ServeWs(hub, w, r)
|
||||
})
|
||||
|
||||
func run(args []string) error {
|
||||
|
||||
flgs, err := newFlags(args)
|
||||
err := http.ListenAndServe(":8080", r)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Errorf("%s", err.Error())
|
||||
log.Fatalf("unable to serve on port 8080")
|
||||
}
|
||||
// setup logger with defaults
|
||||
lgr := logger.NewLogger(flgs.loglvl, "WatchTogether")
|
||||
|
||||
lgr.Infof("go runtime ver: %s", runtime.Version())
|
||||
lgr.Infof("Logging level has been set to %d", lgr.LogLevel)
|
||||
|
||||
mr := server.NewMuxRouter()
|
||||
|
||||
serverDriver := server.NewDriver()
|
||||
|
||||
params := server.NewServerParams(lgr, serverDriver)
|
||||
|
||||
s, err := server.NewServer(mr, params)
|
||||
if err != nil {
|
||||
lgr.Errorf("err: %s", err)
|
||||
lgr.Fatal("error in server.NewServer")
|
||||
}
|
||||
|
||||
s.Addr = fmt.Sprintf(":%d", flgs.port)
|
||||
|
||||
return s.ListenAndServe()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,8 @@ module github.com/qpixel/watchtogether
|
|||
|
||||
go 1.17
|
||||
|
||||
replace (
|
||||
github.com/qpixel/tloghttp => ../../tloghttp
|
||||
github.com/qpixel/tlogbuilder => ../../tlogbuilder
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/peterbourgon/ff/v3 v3.1.2
|
||||
github.com/qpixel/tloghttp v0.0.0-20211222065322-cd8d1a945a36
|
||||
github.com/qpixel/tlogbuilder v0.0.0
|
||||
github.com/ubergeek77/tinylog v1.0.0
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/ubergeek77/tinylog v1.0.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP6JM=
|
||||
github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/ubergeek77/tinylog v1.0.0 h1:gsq98mbig3LDWhsizOe2tid12wHUz/mrkDlmgJ0MZG4=
|
||||
github.com/ubergeek77/tinylog v1.0.0/go.mod h1:NzUi4PkRG2hACL4cGgmW7db6EaKjAeqrqlVQnJdw78Q=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2018 The Go Cloud Development Kit Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @gilcrest - removed TLSServer interface
|
||||
|
||||
// Package driver defines an interface for custom HTTP listeners.
|
||||
// Application code should use package server.
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Server dispatches requests to an http.Handler.
|
||||
type Server interface {
|
||||
// ListenAndServe listens on the TCP network address addr and then
|
||||
// calls Serve with handler to handle requests on incoming connections.
|
||||
// The addr argument will be a non-empty string specifying "host:port".
|
||||
// The http.Handler will always be non-nil.
|
||||
// Drivers must block until serving is done (or
|
||||
// return an error if serving can't occur for some reason), serve
|
||||
// requests to the given http.Handler, and be interruptable by Shutdown.
|
||||
// Drivers should use the given address if they serve using TCP directly.
|
||||
ListenAndServe(addr string, h http.Handler) error
|
||||
|
||||
// Shutdown gracefully shuts down the server without interrupting
|
||||
// any active connections. If the provided context expires before
|
||||
// the shutdown is complete, Shutdown returns the context's error,
|
||||
// otherwise it returns any error returned from closing the Server's
|
||||
// underlying Listener(s).
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"github.com/ubergeek77/tinylog"
|
||||
)
|
||||
|
||||
func NewLogger(lvl int, tag string) *tinylog.Logger {
|
||||
// cfg contains our custom config for tinylog
|
||||
cfg := tinylog.NewConfig()
|
||||
cfg.LogLevel = lvl
|
||||
cfg.LogPrefix = tinylog.GenerateTag(tag, tinylog.NewColor("38;5;133"), cfg)
|
||||
|
||||
//initialize the instance of tinylog
|
||||
lgr := tinylog.NewLogger(cfg)
|
||||
|
||||
return lgr
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/justinas/alice"
|
||||
"github.com/qpixel/tlogbuilder"
|
||||
"github.com/qpixel/tloghttp"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Dan Gillis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// LoggerChain returns a middleware chain (via alice.Chain)
|
||||
// initialized with all the standard middleware handlers for logging. The logger
|
||||
// will be added to the request context for subsequent use with pre-populated
|
||||
// fields, including the request method, url, status, size, duration, remote IP,
|
||||
// user agent, referer. A unique Request ID is also added to the logger, context
|
||||
// and response headers.
|
||||
func (s *Server) loggerChain() alice.Chain {
|
||||
ac := alice.New(tloghttp.NewHandler(s.logger),
|
||||
tloghttp.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
||||
tlogbuilder.CreateBuilder(tloghttp.FromRequest(r)).
|
||||
Info().
|
||||
Str("method", r.Method).
|
||||
Stringer("url", r.URL).
|
||||
Int("status", status).
|
||||
Int("size", size).
|
||||
Dur("duration", duration).
|
||||
Msg("request logged").
|
||||
Build()
|
||||
}),
|
||||
tloghttp.RemoteAddrHandler("remote_ip"),
|
||||
tloghttp.UserAgentHandler("user_agent"),
|
||||
tloghttp.RefererHandler("referer"),
|
||||
//tloghttp.RequestIDHandler("request_id", "Request-Id"),
|
||||
)
|
||||
|
||||
return ac
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package server
|
||||
|
||||
import "net/http"
|
||||
|
||||
const (
|
||||
contentTypeHeaderKey string = "Content-Type"
|
||||
appJSONContentTypeHeaderVal string = "application/json",
|
||||
)
|
||||
|
||||
func (s *Server) routes() {
|
||||
s.router.Handle("/", func(http.ResponseWriter, *http.Response) {
|
||||
|
||||
})
|
||||
}
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
// Copyright 2018 The Go Cloud Development Kit Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// @QPixel edits - I have made a copy of the go-api-basic server code and made
|
||||
// the following changes:
|
||||
//
|
||||
// - removed zerolog dependency
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/qpixel/watchtogether/internal/driver"
|
||||
"github.com/ubergeek77/tinylog"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const pathPrefix = "/api"
|
||||
|
||||
// Server represents an HTTP server
|
||||
type Server struct {
|
||||
router *mux.Router
|
||||
driver driver.Server
|
||||
|
||||
logger *tinylog.Logger
|
||||
|
||||
Addr string
|
||||
|
||||
// Authorization
|
||||
|
||||
// Services
|
||||
}
|
||||
|
||||
//type LoggerService interface {
|
||||
// Read() logger.LoggerResponse
|
||||
//
|
||||
//}
|
||||
|
||||
// SvrParams is the set of configuration parameters for a Server
|
||||
type SvrParams struct {
|
||||
// Logger is used for the server logging
|
||||
Logger *tinylog.Logger
|
||||
|
||||
// Driver serves HTTP requests
|
||||
Driver driver.Server
|
||||
}
|
||||
|
||||
// NewServerParams is an initializer for ServerParams
|
||||
func NewServerParams(lgr *tinylog.Logger, d driver.Server) *SvrParams {
|
||||
options := &SvrParams{
|
||||
Logger: lgr,
|
||||
Driver: d,
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// NewServer initializes a new Server and registers
|
||||
// routes to the router mux
|
||||
// todo add error checking
|
||||
func NewServer(r *mux.Router, params *SvrParams) (*Server, error) {
|
||||
s := &Server{router: r}
|
||||
s.logger = params.Logger
|
||||
s.driver = params.Driver
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() error {
|
||||
if s.Addr == "" {
|
||||
return errors.New("server Addr is empty")
|
||||
}
|
||||
if s.router == nil {
|
||||
return errors.New("server router is nil")
|
||||
}
|
||||
if s.driver == nil {
|
||||
return errors.New("server driver is nil")
|
||||
}
|
||||
s.logger.Infof("server is listening on %s", s.Addr)
|
||||
return s.driver.ListenAndServe(s.Addr, s.router)
|
||||
}
|
||||
|
||||
// Shutdown will gracefully shut down the server without interrupting any active connections
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
if s.driver == nil {
|
||||
return nil
|
||||
}
|
||||
return s.driver.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Driver implements the driver.Server interface. The zero value is a valid http.Server
|
||||
type Driver struct {
|
||||
Server http.Server
|
||||
}
|
||||
|
||||
// NewDriver intializes a Driver with http.Server using default timeouts
|
||||
func NewDriver() *Driver {
|
||||
return &Driver{
|
||||
Server: http.Server{
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServe sets the address and handler on Driver's http.Server
|
||||
func (d *Driver) ListenAndServe(addr string, h http.Handler) error {
|
||||
d.Server.Addr = addr
|
||||
d.Server.Handler = h
|
||||
return d.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the server without interrupting any active connections,
|
||||
// by calling Shutdown on Driver's http.Server
|
||||
func (d *Driver) Shutdown(ctx context.Context) error {
|
||||
return d.Server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// NewMuxRouter initializes a gorilla/mux router and
|
||||
// adds the /api subroute to it
|
||||
func NewMuxRouter() *mux.Router {
|
||||
// initializer gorilla/mux router
|
||||
r := mux.NewRouter()
|
||||
|
||||
// send Router through PathPrefix method to validate any standard
|
||||
// subroutes you may want for your APIs. e.g. I always want to be
|
||||
// sure that every request has "/api" as part of its path prefix
|
||||
// without having to put it into every handle path in my various
|
||||
// routing functions
|
||||
s := r.PathPrefix(pathPrefix).Subrouter()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func decoderErr(err error) error {
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
return errors.New("request body cannot be empty")
|
||||
case err == io.ErrUnexpectedEOF:
|
||||
return errors.New("malformed json")
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package ws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// time allowed to write a message to the peer.
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
pongWait = 60 * time.Second
|
||||
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
var (
|
||||
newline = []byte{'\n'}
|
||||
space = []byte{' '}
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
hub *Hub
|
||||
conn *websocket.Conn
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
// readPump pumps messages from the websocket connection to the hub.
|
||||
//
|
||||
// The application runs readPump in a per-connection goroutine. The application
|
||||
// ensures that there is at most one reader on a connection by executing all
|
||||
// reads from this goroutine.
|
||||
func (c *Client) readPump() {
|
||||
defer func() {
|
||||
c.hub.unregister <- c
|
||||
c.conn.Close()
|
||||
}()
|
||||
c.conn.SetReadLimit(maxMessageSize)
|
||||
c.conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||
log.Infof("error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
|
||||
c.hub.broadcast <- RawMessage{
|
||||
Client: c,
|
||||
Data: message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writePump pumps messages from the hub to the websocket connection.
|
||||
//
|
||||
// A goroutine running writePump is started for each connection. The
|
||||
// application ensures that there is at most one writer to a connection by
|
||||
// executing all writes from this goroutine.
|
||||
func (c *Client) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.conn.Close()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.send:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if !ok {
|
||||
// The hub closed the channel.
|
||||
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
w, err := c.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.Write(message)
|
||||
|
||||
// Add queued chat messages to the current websocket message.
|
||||
n := len(c.send)
|
||||
for i := 0; i < n; i++ {
|
||||
w.Write(newline)
|
||||
w.Write(<-c.send)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ServeWs handles websocket requests from the peer.
|
||||
func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||
upgrader.CheckOrigin = func(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
||||
client.hub.register <- client
|
||||
|
||||
// Allow collection of memory referenced by the caller by doing all work in
|
||||
// new goroutines.
|
||||
go client.writePump()
|
||||
go client.readPump()
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package ws
|
||||
|
||||
type IdentityData struct {
|
||||
ClientID string `json:"client_id"`
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
func handleIdentifyEvent(message *Message) {
|
||||
|
||||
}
|
||||
|
||||
func handlePingEvent(message *Message) {
|
||||
m := Message{
|
||||
message.Client,
|
||||
MessageData{
|
||||
Type: Pong,
|
||||
Data: nil,
|
||||
},
|
||||
}
|
||||
message.send <- m.SerializeMessage().Data
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package ws
|
||||
|
||||
type Hub struct {
|
||||
// Registered clients
|
||||
clients map[*Client]bool
|
||||
|
||||
// Inbound messages from the clients
|
||||
broadcast chan RawMessage
|
||||
|
||||
// Register requests from the clients
|
||||
register chan *Client
|
||||
|
||||
// Unregister requests from clients
|
||||
unregister chan *Client
|
||||
}
|
||||
|
||||
func NewHub() *Hub {
|
||||
return &Hub{
|
||||
broadcast: make(chan RawMessage),
|
||||
register: make(chan *Client),
|
||||
unregister: make(chan *Client),
|
||||
clients: make(map[*Client]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) handleMessage(rm RawMessage) {
|
||||
m := rm.UnSerializeData()
|
||||
switch m.Type {
|
||||
case Identify:
|
||||
handleIdentifyEvent(&m)
|
||||
case Ping:
|
||||
handlePingEvent(&m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Hub) Run() {
|
||||
for {
|
||||
select {
|
||||
case client := <-h.register:
|
||||
h.clients[client] = true
|
||||
case client := <-h.unregister:
|
||||
if _, ok := h.clients[client]; ok {
|
||||
delete(h.clients, client)
|
||||
close(client.send)
|
||||
}
|
||||
case message := <-h.broadcast:
|
||||
go h.handleMessage(message)
|
||||
//for client := range h.clients {
|
||||
// select {
|
||||
// case client.send <- message:
|
||||
// default:
|
||||
// close(client.send)
|
||||
// delete(h.clients, client)
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package ws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
tlog "github.com/ubergeek77/tinylog"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var log = tlog.NewTaggedLogger("WS", tlog.NewColor("38;5;111"))
|
||||
|
||||
type MessageTypes float64
|
||||
|
||||
// todo rewrite to use an event handler system
|
||||
const (
|
||||
Ping MessageTypes = iota
|
||||
Pong
|
||||
Identify
|
||||
Position
|
||||
SetPosition
|
||||
)
|
||||
|
||||
type MessageData struct {
|
||||
Type MessageTypes `json:"type"`
|
||||
RawData json.RawMessage `json:"data,omitempty"`
|
||||
Data map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
*Client
|
||||
MessageData
|
||||
}
|
||||
|
||||
type RawMessage struct {
|
||||
Client *Client
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (rm RawMessage) UnSerializeData() Message {
|
||||
s, _ := strconv.Unquote(string(rm.Data))
|
||||
var md MessageData
|
||||
if err := json.Unmarshal([]byte(s), &md); err != nil {
|
||||
log.Errorf("error unmarshalling message, %s", err.Error())
|
||||
}
|
||||
if md.RawData != nil && len(md.RawData) > 0 && md.RawData[0] == '"' {
|
||||
var s string
|
||||
if err := json.Unmarshal(md.RawData, &s); err != nil {
|
||||
// handle error
|
||||
log.Errorf("error unmarshalling message, %s", err.Error())
|
||||
}
|
||||
md.RawData = json.RawMessage(s)
|
||||
if err := json.Unmarshal(md.RawData, &md.Data); err != nil {
|
||||
// handle error
|
||||
log.Errorf("error unmarshalling message, %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
m := Message{
|
||||
Client: rm.Client,
|
||||
MessageData: md,
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Message) SerializeMessage() RawMessage {
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
log.Errorf("unable to marshal message, %s", err.Error())
|
||||
}
|
||||
return RawMessage{
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ws
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
UserID string `json:"user_id"`
|
||||
Admin bool `json:"admin"`
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package services
|
||||
|
||||
import "github.com/ubergeek77/tinylog"
|
||||
|
||||
// LoggerService reads and updates the logger state
|
||||
type LoggerService struct {
|
||||
Logger tinylog.Logger
|
||||
}
|
||||
|
||||
// NewLoggerService creates a new instance of the logging service
|
||||
func NewLoggerService(logger tinylog.Logger) *LoggerService {
|
||||
return &LoggerService{Logger: logger}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
enum MessageTypes {
|
||||
Ping,
|
||||
Pong,
|
||||
Identify,
|
||||
Position,
|
||||
SetPosition,
|
||||
}
|
||||
|
||||
interface IMessage {
|
||||
Type: MessageTypes;
|
||||
Data?: Map<string, unknown>;
|
||||
}
|
||||
|
||||
export default IMessage;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
interface IUser {
|
||||
name: string;
|
||||
userID: string;
|
||||
admin: boolean;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
interface IdentityData {
|
||||
clientID: string;
|
||||
user: IUser;
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ const AdminPage: NextPage<AdminPageProps> = ({
|
|||
isAdmin,
|
||||
session,
|
||||
}) => {
|
||||
console.log(session);
|
||||
return (
|
||||
<>
|
||||
<Container height="100vh">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
Arguments:
|
||||
/home/pixel/.volta/tools/image/node/16.13.2/bin/node /home/pixel/.volta/tools/image/yarn/1.22.17/bin/yarn.js add -D @types/zib
|
||||
|
||||
PATH:
|
||||
/home/pixel/.volta/tools/image/yarn/1.22.17/bin:/home/pixel/.volta/tools/image/node/16.13.2/bin:/home/pixel/.volta/bin:/home/pixel/.volta/bin:/home/pixel/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
|
||||
|
||||
Yarn version:
|
||||
1.22.17
|
||||
|
||||
Node version:
|
||||
16.13.2
|
||||
|
||||
Platform:
|
||||
linux x64
|
||||
|
||||
Trace:
|
||||
Error: https://registry.yarnpkg.com/@types%2fzib: Not found
|
||||
at Request.params.callback [as _callback] (/home/pixel/.volta/tools/image/yarn/1.22.17/lib/cli.js:67029:18)
|
||||
at Request.self.callback (/home/pixel/.volta/tools/image/yarn/1.22.17/lib/cli.js:140883:22)
|
||||
at Request.emit (node:events:390:28)
|
||||
at Request.<anonymous> (/home/pixel/.volta/tools/image/yarn/1.22.17/lib/cli.js:141855:10)
|
||||
at Request.emit (node:events:390:28)
|
||||
at IncomingMessage.<anonymous> (/home/pixel/.volta/tools/image/yarn/1.22.17/lib/cli.js:141777:12)
|
||||
at Object.onceWrapper (node:events:509:28)
|
||||
at IncomingMessage.emit (node:events:402:35)
|
||||
at endReadableNT (node:internal/streams/readable:1343:12)
|
||||
at processTicksAndRejections (node:internal/process/task_queues:83:21)
|
||||
|
||||
npm manifest:
|
||||
No manifest
|
||||
|
||||
yarn manifest:
|
||||
No manifest
|
||||
|
||||
Lockfile:
|
||||
No lockfile
|
||||
Loading…
Reference in New Issue