Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# The base container already has entrypoint, vscode user account, etc. out of the box
FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu-22.04

# Containerlab version will be set in devcontainer.json
ARG _CLAB_VERSION

# Set permissions for mounts in devcontainer.json
RUN mkdir -p /home/vscode/.vscode-server/bin
RUN chown -R vscode:vscode /home/vscode/.vscode-server

# Update and install some basic tools inside the container
# Adjust this list based on your demands
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y \
sshpass \
curl \
wget \
openssl \
iputils-ping \
nmap \
htop \
nano \
vim \
tmux \
nload \
yamllint \
zsh \
file \
&& apt-get clean

# Install oh-my-zsh for more terminal features and set is as primary shell
ENV SHELL /bin/zsh
RUN wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh || true

# Set editor
RUN echo "export VISUAL='nano'" >> /home/vscode/.zshrc
RUN echo "export EDITOR='nano'" >> /home/vscode/.zshrc
42 changes: 42 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "broadcastbox-develop",
"build": {
"dockerfile": "Dockerfile"
},
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.23"
},
"ghcr.io/devcontainers/features/docker-in-docker:2.11": {
"version": "latest",
"dockerDashComposeVersion": "v2"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20.18"
}
},
// add any required extensions that must be pre-installed in the devcontainer
"customizations": {
"vscode": {
"extensions": [
"golang.go",
"ms-azuretools.vscode-docker",
"EditorConfig.EditorConfig",
"eamodio.gitlens",
"ms-vscode.makefile-tools",
"redhat.vscode-yaml"
]
}
},
"mounts": [
// Source: https://github.com/microsoft/vscode-remote-release/issues/5823
// Creates a named volume mounted to /home/vscode. Survives a rebuild.
// Creates a named volume mounted to /root/.docker. Survives a rebuild.
// Creates an anonymous volume mounted to /home/vscode/.vscode-server. Gets destroyed on rebuild, which allows vscode to reinstall the extensions and dotfiles.
"source=broadcast-box-vscode-home-dir,target=/home/vscode,type=volume",
"source=broadcast-box-docker-root-config,target=/root/.docker,type=volume",
"target=/home/vscode/.vscode-server,type=volume"
]
}
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
HTTP_ADDRESS=":8080"
ENABLE_HTTP_REDIRECT=
REACT_APP_API_PATH="http://localhost:8080/api"
WEBHOOK_URL=http://127.0.0.1:8090/internal/request-stream
WEBHOOK_TIMEOUT=5000

# /etc/letsencrypt/live/<your-domain-name>/privkey.pem
SSL_KEY=
Expand Down
3 changes: 3 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
HTTP_ADDRESS=":8080"
ENABLE_HTTP_REDIRECT=
REACT_APP_API_PATH="/api"
# Use if authentication for the streamer is desired (to prevent unauthorized people from streaming)
#WEBHOOK_URL=http://authentication-backend:8090/webhook
#WEBHOOK_TIMEOUT=5000

# /etc/letsencrypt/live/<your-domain-name>/privkey.pem
SSL_KEY=
Expand Down
13 changes: 12 additions & 1 deletion .github/workflows/build-container.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,22 @@ jobs:
tags: |
type=raw,value=latest

- name: Build and push
- name: Build and push streaming server
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

# Not working yet
- name: Build and push authentication-backend
uses: docker/build-push-action@v6
with:
context: .
dockerfile: authentication-backend/Dockerfile
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ broadcast-box
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Pocketbase
authentication-backend/pb_data
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch authentication-backend",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "authentication-backend/main.go",
"args": ["serve"]
}
]
}
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ the latency of 120 milliseconds observed.

## Getting Started

Broadcast Box is made up of two parts. The server is written in Go and is in charge of ingesting and broadcasting WebRTC. The frontend is in react and connects to the Go backend. The Go server can be used to serve the HTML/CSS/JS directly. Use the following instructions to build from source or utilize [Docker](#docker) / [Docker Compose](#docker-compose).
Broadcast Box is made up of three parts. The server is written in Go and is in charge of ingesting and broadcasting WebRTC. The authentication-backend is written in go and utilizes pocketbase to abstract most api endpoints.The frontend is in react and connects to the Go backend. The Go server can be used to serve the HTML/CSS/JS directly. Use the following instructions to build from source or utilize [Docker](#docker) / [Docker Compose](#docker-compose).

### Configuring

Expand Down Expand Up @@ -176,6 +176,12 @@ To run the Go server, run `go run .` in the root of this project, you should see

To use Broadcast Box navigate to: `http://<YOUR_IP>:8080`. In your broadcast tool of choice, you will broadcast to `http://<YOUR_IP>:8080/api/whip`.

#### Authetication-backend
The authentication backend can be started by going into the `authentication-backend` folder and run:
```shell
go run main.go serve
```

### Docker

A Docker image is also provided to make it easier to run locally and in production. The arguments you run the Dockerfile with depending on
Expand Down
21 changes: 21 additions & 0 deletions authentication-backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM golang:1.23 AS builder

WORKDIR /app
RUN mkdir /artifacts

COPY go.mod .
COPY go.sum .

RUN go mod download

COPY authentication-backend authentication-backend

RUN CGO_ENABLED=0 GOOS=linux go build -o /artifacts/authentication-backend authentication-backend/main.go

FROM scratch

COPY --from=builder /artifacts/authentication-backend authentication-backend

ENTRYPOINT [ "./authentication-backend" ]
CMD [ "serve" ]
EXPOSE 8090
47 changes: 47 additions & 0 deletions authentication-backend/internal/internal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package internal

import (
"encoding/json"
"io"
"net/http"

"github.com/glimesh/broadcast-box/internal/webrtc"
"github.com/labstack/echo/v5"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core"
)

type basicDBUser struct {
Username string `json:"username" db:"username"`
}

func HandleServeEvent(e *core.ServeEvent) error {
e.Router.POST("/internal/request-stream", func(c echo.Context) error {
payload := webrtc.WebhookPayload{}
body, err := io.ReadAll(c.Request().Body)
if err != nil {
e.App.Logger().Error("Error reading request body: " + err.Error())
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Error reading request body"})
}
json.Unmarshal(body, &payload)
streamkey := payload.StreamKey
e.App.Logger().Debug("Requesting stream with key: " + streamkey)

user := &basicDBUser{}
err = e.App.Dao().DB().
Select("username").
From("users").
InnerJoin("streamkeys", dbx.NewExp("users.streamkey_ID = streamkeys.id")).
Where(dbx.NewExp("streamkey = {:input}", dbx.Params{"input": streamkey})).One(user)
if err != nil {
e.App.Logger().Error("Error finding user: " + err.Error())
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Error finding user"})
}

e.App.Logger().Debug("User found: " + user.Username)
response := webrtc.WebhookResponse{Username: user.Username}
return c.JSON(http.StatusOK, response)
} /* optional middlewares */)

return nil
}
32 changes: 32 additions & 0 deletions authentication-backend/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"log"
"os"
"strings"

"github.com/glimesh/broadcast-box/authentication-backend/internal"
_ "github.com/glimesh/broadcast-box/authentication-backend/migrations"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
)

func main() {
app := pocketbase.New()

// loosely check if it was executed using "go run"
isGoRun := strings.HasPrefix(os.Args[0], os.TempDir())

migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
// enable auto creation of migration files when making collection changes in the Admin UI
// (the isGoRun check is to enable it only during development)
Automigrate: isGoRun,
})

app.OnBeforeServe().Add(internal.HandleServeEvent)

if err := app.Start(); err != nil {
log.Fatal(err)
}

}
62 changes: 62 additions & 0 deletions authentication-backend/migrations/1728162217_created_streamkeys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package migrations

import (
"encoding/json"

"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)

func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `{
"id": "bl7jh9ptmhrj95q",
"created": "2024-10-05 21:03:37.812Z",
"updated": "2024-10-05 21:03:37.812Z",
"name": "streamkeys",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "90ugkcyb",
"name": "streamkey",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}`

collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}

return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);

collection, err := dao.FindCollectionByNameOrId("bl7jh9ptmhrj95q")
if err != nil {
return err
}

return dao.DeleteCollection(collection)
})
}
57 changes: 57 additions & 0 deletions authentication-backend/migrations/1728162258_updated_users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package migrations

import (
"encoding/json"

"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)

func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db)

collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}

// add
new_streamkey := &schema.SchemaField{}
if err := json.Unmarshal([]byte(`{
"system": false,
"id": "yb8cpxlq",
"name": "streamkey_ID",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "bl7jh9ptmhrj95q",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}`), new_streamkey); err != nil {
return err
}
collection.Schema.AddField(new_streamkey)

return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db)

collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}

// remove
collection.Schema.RemoveField("yb8cpxlq")

return dao.SaveCollection(collection)
})
}
Loading