Skip to content

Commit

Permalink
Merge pull request #69 from weni-ai/domains-restriction
Browse files Browse the repository at this point in the history
Domains restriction
  • Loading branch information
rasoro authored Jan 23, 2025
2 parents 39a4742 + 369c4dc commit d3a0ba6
Show file tree
Hide file tree
Showing 17 changed files with 354 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: ci
on: [push, pull_request]
env:
go-version: '1.17.x'
go-version: '1.23'
jobs:
test:
name: Test
Expand Down
14 changes: 13 additions & 1 deletion api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/go-redis/redis/v8"
"github.com/ilhasoft/wwcs/config"
"github.com/ilhasoft/wwcs/pkg/db"
"github.com/ilhasoft/wwcs/pkg/flows"
"github.com/ilhasoft/wwcs/pkg/history"
"github.com/ilhasoft/wwcs/pkg/metric"
"github.com/ilhasoft/wwcs/pkg/queue"
Expand Down Expand Up @@ -87,7 +88,18 @@ func main() {

clientM := websocket.NewClientManager(rdb, int(queueConfig.ClientTTL))

app := websocket.NewApp(websocket.NewPool(), rdb, mdb, metrics, histories, clientM, queueConn)
flowsClient := flows.NewClient(config.Get().FlowsURL)

app := websocket.NewApp(
websocket.NewPool(),
rdb,
mdb,
metrics,
histories,
clientM,
queueConn,
flowsClient,
)
app.StartConnectionsHeartbeat()
websocket.SetupRoutes(app)

Expand Down
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ type Configuration struct {
RedisQueue RedisQueue
SentryDSN string `env:"WWC_APP_SENTRY_DSN"`
DB DB

RestrictDomains bool `default:"false" env:"WWC_RESTRICT_DOMAINS"`

FlowsURL string `default:"https://flows.weni.ai" env:"WWC_FLOWS_URL"`
MemCacheTimeout int64 `default:"5" env:"WWC_MEM_CACHE_TIMEOUT"`
}

type S3 struct {
Expand Down
6 changes: 6 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ var ttDefaultConfigs = Configuration{
ContextTimeout: 15,
HealthcheckTimeout: 15,
},
RestrictDomains: false,
FlowsURL: "https://flows.weni.ai",
MemCacheTimeout: 5,
}

var ttEnvConfigs = Configuration{
Expand Down Expand Up @@ -69,6 +72,9 @@ var ttEnvConfigs = Configuration{
ContextTimeout: 15,
HealthcheckTimeout: 15,
},
RestrictDomains: false,
FlowsURL: "https://flows.weni.ai",
MemCacheTimeout: 5,
}

var requiredEnvCases = map[string]string{
Expand Down
7 changes: 3 additions & 4 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.17.11-alpine3.16 AS base
FROM golang:1.23-bookworm AS base

ARG APP_UID=1000
ARG APP_GID=1000
Expand All @@ -11,7 +11,6 @@ ARG BUILD_DEPS="\

ARG RUNTIME_DEPS="\
curl \
su-exec \
bash"

ARG WWC_PORT="8000"
Expand Down Expand Up @@ -45,7 +44,7 @@ WORKDIR ${PROJECT_PATH}

FROM base AS build

RUN if [ ! "x${BUILD_DEPS}" = "x" ] ; then apk add --no-cache ${BUILD_DEPS}; fi
RUN if [ ! "x${BUILD_DEPS}" = "x" ] ; then apt install ${BUILD_DEPS}; fi

# Copy and download dependency using go mod
COPY go.mod .
Expand All @@ -63,7 +62,7 @@ FROM base
# copy project
COPY --from=build --chown=app_user:app_group ${PROJECT_PATH}/${APPLICATION_NAME} ${PROJECT_PATH}

RUN if [ ! "x${RUNTIME_DEPS}" = "x" ] ; then apk add --no-cache ${RUNTIME_DEPS}; fi
RUN if [ ! "x${RUNTIME_DEPS}" = "x" ] ; then apt install ${RUNTIME_DEPS}; fi

COPY docker/docker-entrypoint.sh .

Expand Down
4 changes: 2 additions & 2 deletions docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ bootstrap_conf(){
bootstrap_conf

if [[ "start" == "$1" ]]; then
exec su-exec "${APP_UID}:${APP_GID}" "./${APPLICATION_NAME}"
exec "./${APPLICATION_NAME}"
elif [[ "healthcheck" == "$1" ]]; then
su-exec "${APP_UID}:${APP_GID}" curl -SsLf "http://127.0.0.1:${WWC_PORT}/healthcheck" -o /tmp/null --connect-timeout 3 --max-time 20 -w "%{http_code} %{http_version} %{response_code} %{time_total}\n" || exit 1
curl -SsLf "http://127.0.0.1:${WWC_PORT}/healthcheck" -o /tmp/null --connect-timeout 3 --max-time 20 -w "%{http_code} %{http_version} %{response_code} %{time_total}\n" || exit 1
exit 0
fi

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/ilhasoft/wwcs

go 1.17
go 1.23

require (
github.com/adjust/rmq/v4 v4.0.1
Expand Down
11 changes: 11 additions & 0 deletions local_test_webchat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
(function (d, s, u) {
let h = d.getElementsByTagName(s)[0], k = d.createElement(s);
k.onload = function () {
let l = d.createElement(s); l.src = u; l.async = true;
h.parentNode.insertBefore(l, k.nextSibling);
};
k.async = true; k.src = 'https://storage.googleapis.com/push-webchat/wwc-latest.js';
h.parentNode.insertBefore(k, h);
})(document, 'script', './script.js');
</script>
42 changes: 42 additions & 0 deletions pkg/flows/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package flows

import (
"encoding/json"
"fmt"
"net/http"
)

type IClient interface {
GetChannelAllowedDomains(string) ([]string, error)
}

type Client struct {
BaseURL string `json:"base_url"`
}

func NewClient(baseURL string) *Client {
return &Client{
BaseURL: baseURL,
}
}

func (c *Client) GetChannelAllowedDomains(channelUUID string) ([]string, error) {
url := fmt.Sprintf("%s/api/v2/internals/channel_allowed_domains?channel=%s", c.BaseURL, channelUUID)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get channel allowed domains, status code: %d", resp.StatusCode)
}

var domains []string
err = json.NewDecoder(resp.Body).Decode(&domains)
if err != nil {
return nil, err
}

return domains, nil
}
52 changes: 52 additions & 0 deletions pkg/flows/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package flows

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGetChannelAllowedDomains(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("[\"domain1.com\", \"domain2.com\"]"))
}))
defer server.Close()

client := Client{BaseURL: server.URL}

domains, err := client.GetChannelAllowedDomains("09bf3dee-973e-43d3-8b94-441406c4a565")

assert.NoError(t, err)
assert.Equal(t, 2, len(domains))
}

func TestGetChannelAllowedDomainsStatus404(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer server.Close()

client := Client{BaseURL: server.URL}

_, err := client.GetChannelAllowedDomains("09bf3dee-973e-43d3-8b94-441406c4a565")

assert.Equal(t, err.Error(), "failed to get channel allowed domains, status code: 404")
}

func TestGetChannelAllowedDomainsStatusWithNoDomain(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("[]"))
}))
defer server.Close()

client := Client{BaseURL: server.URL}

domains, err := client.GetChannelAllowedDomains("09bf3dee-973e-43d3-8b94-441406c4a565")

assert.NoError(t, err)
assert.Equal(t, 0, len(domains))
}
49 changes: 49 additions & 0 deletions pkg/memcache/memcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package memcache

import (
"sync"
"time"
)

type Cache[K comparable, V any] struct {
items map[K]item[V]
mu sync.Mutex
}

type item[V any] struct {
value V
expiry time.Time
deleted bool
}

func New[K comparable, V any]() *Cache[K, V] {
return &Cache[K, V]{
items: make(map[K]item[V]),
}
}

func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = item[V]{
value: value,
expiry: time.Now().Add(ttl),
}
}

func (c *Cache[K, V]) Get(key K) (V, bool) {
c.mu.Lock()
defer c.mu.Unlock()
item, found := c.items[key]
if !found || time.Now().After(item.expiry) || item.deleted {
delete(c.items, key)
return item.value, false
}
return item.value, true
}

func (c *Cache[K, V]) Remove(key K) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.items, key)
}
26 changes: 26 additions & 0 deletions pkg/memcache/memcache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package memcache

import (
"testing"
"time"
)

func TestCache(t *testing.T) {
cacheDomains := New[string, []string]()
chanUUID := "5f610454-98c1-4a54-9499-e2d2b9b68334"
cacheDomains.Set(chanUUID, []string{"127.0.0.1", "localhost"}, time.Duration(time.Second*2))

chan1domains, ok := cacheDomains.Get(chanUUID)
if !ok {
t.Error("Expected channel UUID to be found")
}
if len(chan1domains) != 2 {
t.Error("Expected 2 domains in cache, got", len(chan1domains))
}
time.Sleep(3 * time.Second)

_, ok = cacheDomains.Get(chanUUID)
if ok {
t.Error("Expected channel UUID not to be found")
}
}
5 changes: 4 additions & 1 deletion pkg/websocket/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/go-redis/redis/v8"
"github.com/ilhasoft/wwcs/pkg/flows"
"github.com/ilhasoft/wwcs/pkg/history"
"github.com/ilhasoft/wwcs/pkg/metric"
"github.com/ilhasoft/wwcs/pkg/queue"
Expand All @@ -21,10 +22,11 @@ type App struct {
Histories history.Service
ClientManager ClientManager
QueueConnectionManager queue.Connection
FlowsClient flows.IClient
}

// Create new App instance.
func NewApp(pool *ClientPool, rdb *redis.Client, mdb *mongo.Database, metrics *metric.Service, histories history.Service, clientM ClientManager, qconnM queue.Connection) *App {
func NewApp(pool *ClientPool, rdb *redis.Client, mdb *mongo.Database, metrics *metric.Service, histories history.Service, clientM ClientManager, qconnM queue.Connection, fc flows.IClient) *App {
return &App{
ClientPool: pool,
RDB: rdb,
Expand All @@ -33,6 +35,7 @@ func NewApp(pool *ClientPool, rdb *redis.Client, mdb *mongo.Database, metrics *m
Histories: histories,
ClientManager: clientM,
QueueConnectionManager: qconnM,
FlowsClient: fc,
}
}

Expand Down
Loading

0 comments on commit d3a0ba6

Please sign in to comment.