feat: initial commit

This commit is contained in:
2023-12-13 20:07:22 +01:00
commit 5d0311b731
79 changed files with 3143 additions and 0 deletions

81
pkg/client/api.go Normal file
View File

@ -0,0 +1,81 @@
package client
import (
"bytes"
"context"
"encoding/json"
"net/http"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
func (c *Client) apiGet(ctx context.Context, url string, result any, funcs ...HTTPOptionFunc) error {
if err := c.apiDo(ctx, http.MethodGet, url, nil, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiPost(ctx context.Context, url string, payload any, result any, funcs ...HTTPOptionFunc) error {
if err := c.apiDo(ctx, http.MethodPost, url, payload, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiDelete(ctx context.Context, url string, payload any, result any, funcs ...HTTPOptionFunc) error {
if err := c.apiDo(ctx, http.MethodDelete, url, payload, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiDo(ctx context.Context, method string, url string, payload any, response any, funcs ...HTTPOptionFunc) error {
opts := NewHTTPOptions(funcs...)
logger.Debug(
ctx, "new http request",
logger.F("method", method),
logger.F("url", url),
logger.F("payload", payload),
)
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
if err := encoder.Encode(payload); err != nil {
return errors.WithStack(err)
}
req, err := http.NewRequest(method, url, &buf)
if err != nil {
return errors.WithStack(err)
}
for key, values := range opts.Headers {
for _, v := range values {
req.Header.Add(key, v)
}
}
res, err := c.http.Do(req)
if err != nil {
return errors.WithStack(err)
}
defer res.Body.Close()
decoder := json.NewDecoder(res.Body)
if err := decoder.Decode(&api.Response{Data: &response}); err != nil {
return errors.WithStack(err)
}
return nil
}

27
pkg/client/cast.go Normal file
View File

@ -0,0 +1,27 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/arcad/arcast/pkg/server"
"github.com/pkg/errors"
)
type Status = server.StatusResponse
func (c *Client) Cast(ctx context.Context, addr string, url string) (*Status, error) {
endpoint := fmt.Sprintf("http://%s/api/v1/cast", addr)
req := &server.CastRequest{
URL: url,
}
res := &server.StatusResponse{}
if err := c.apiPost(ctx, endpoint, &req, &res); err != nil {
return nil, errors.WithStack(err)
}
return res, nil
}

15
pkg/client/client.go Normal file
View File

@ -0,0 +1,15 @@
package client
import (
"net/http"
)
type Client struct {
http *http.Client
}
func New() *Client {
return &Client{
http: &http.Client{},
}
}

43
pkg/client/options.go Normal file
View File

@ -0,0 +1,43 @@
package client
import "net/http"
type HTTPOptions struct {
Headers http.Header
}
type HTTPOptionFunc func(opts *HTTPOptions)
func NewHTTPOptions(funcs ...HTTPOptionFunc) *HTTPOptions {
opts := &HTTPOptions{}
for _, fn := range funcs {
fn(opts)
}
return opts
}
type ScanOptions struct {
PlayerIDs []string
}
func NewScanOptions(funcs ...ScanOptionFunc) *ScanOptions {
opts := &ScanOptions{
PlayerIDs: make([]string, 0),
}
for _, fn := range funcs {
fn(opts)
}
return opts
}
type ScanOptionFunc func(opts *ScanOptions)
func WithPlayerIDs(ids ...string) ScanOptionFunc {
return func(opts *ScanOptions) {
opts.PlayerIDs = ids
}
}

11
pkg/client/player.go Normal file
View File

@ -0,0 +1,11 @@
package client
import (
"net"
)
type Player struct {
ID string
IPs []net.IP
Port int
}

21
pkg/client/reset.go Normal file
View File

@ -0,0 +1,21 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/arcad/arcast/pkg/server"
"github.com/pkg/errors"
)
func (c *Client) Reset(ctx context.Context, addr string) (*Status, error) {
endpoint := fmt.Sprintf("http://%s/api/v1/cast", addr)
res := &server.StatusResponse{}
if err := c.apiDelete(ctx, endpoint, nil, &res); err != nil {
return nil, errors.WithStack(err)
}
return res, nil
}

69
pkg/client/scan.go Normal file
View File

@ -0,0 +1,69 @@
package client
import (
"context"
"slices"
"forge.cadoles.com/arcad/arcast/pkg/server"
"github.com/grandcat/zeroconf"
"github.com/pkg/errors"
)
func (c *Client) Scan(ctx context.Context, funcs ...ScanOptionFunc) ([]*Player, error) {
opts := NewScanOptions(funcs...)
resolver, err := zeroconf.NewResolver(nil)
if err != nil {
return nil, errors.WithStack(err)
}
done := make(chan struct{})
players := make([]*Player, 0)
entries := make(chan *zeroconf.ServiceEntry)
go func(results <-chan *zeroconf.ServiceEntry) {
defer close(done)
for entry := range results {
addPlayer := func() {
players = append(players, &Player{
ID: entry.Instance,
IPs: entry.AddrIPv4,
Port: entry.Port,
})
}
searchedLen := len(opts.PlayerIDs)
if searchedLen == 0 {
addPlayer()
continue
}
if slices.Contains(opts.PlayerIDs, entry.Instance) {
addPlayer()
}
if searchedLen == len(players) {
break
}
}
done <- struct{}{}
}(entries)
err = resolver.Browse(ctx, server.MDNSService, server.MDNSDomain, entries)
if err != nil {
return nil, errors.WithStack(err)
}
select {
case <-done:
return players, nil
case <-ctx.Done():
if err := ctx.Err(); err != nil {
return players, errors.WithStack(err)
}
return players, nil
}
}

21
pkg/client/status.go Normal file
View File

@ -0,0 +1,21 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/arcad/arcast/pkg/server"
"github.com/pkg/errors"
)
func (c *Client) Status(ctx context.Context, addr string) (*Status, error) {
endpoint := fmt.Sprintf("http://%s/api/v1/status", addr)
res := &server.StatusResponse{}
if err := c.apiGet(ctx, endpoint, res); err != nil {
return nil, errors.WithStack(err)
}
return res, nil
}