219 lines
9.0 KiB
Markdown
219 lines
9.0 KiB
Markdown
# Scratching Post — Sunbeam Digital Signage
|
|
|
|
A self-hosted digital notice board / signage system built on ASP.NET Core 8, designed for IIS on Windows Server 2019/2022.
|
|
|
|
## Terminology
|
|
|
|
| Component | Name | Description |
|
|
|-----------|------|-------------|
|
|
| Individual slide | **Meow** | A single content slide — WYSIWYG, embed, or calendar |
|
|
| Slide collection | **PURR** | Persistent User Requests and Reminders — a device playlist |
|
|
| Priority slide | **ASK** | Attention Seeking Kitty — overrides collection order *(planned)* |
|
|
| Display node | **Kitten** | A device that plays a playlist of Meows |
|
|
| Backend | **Scratching Post** | The admin panel where all content is managed |
|
|
| Framework | **Sunbeam** | The underlying system powering everything |
|
|
|
|
## Features
|
|
|
|
- **WYSIWYG Editor** — TinyMCE with image upload, tables, rich text
|
|
- **Embed Web Content** — iframe any URL as a slide
|
|
- **ICS Calendar** — display upcoming events from any .ics feed
|
|
- **Device Playlists** — assign slides to specific devices with individual durations
|
|
- **Per-Device Resolution** — configure display resolution per device
|
|
- **URL-Based Devices** — access displays via `http://server/frontofhouse`, `http://server/staffroom`
|
|
- **Drag-and-Drop Playlist** — reorder slides in the admin
|
|
- **Live Updates** — display polls for changes every 30 seconds
|
|
- **Full-Screen Display** — hidden cursor, clock overlay, progress bar
|
|
- **Smooth Transitions** — fade or slide transitions
|
|
- **Meow Expiration** — give any slide an expiry date/time; it's automatically skipped on displays afterwards (and stays in the library, unedited, in case you need it again)
|
|
- **Docker Support** — run via `docker compose up` as an alternative to IIS, with volumes for the database and uploads
|
|
|
|
## Requirements
|
|
|
|
**IIS (Windows Server):**
|
|
- Windows Server 2019 or 2022
|
|
- IIS with ASP.NET Core Hosting Bundle
|
|
- .NET 8 SDK (for building) / .NET 8 Runtime (for hosting)
|
|
|
|
**Docker (any host):**
|
|
- Docker Engine + Docker Compose — see [Docker Deployment](#docker-deployment) below
|
|
|
|
## Quick Start
|
|
|
|
### 1. Install Prerequisites
|
|
|
|
Download and install:
|
|
- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
|
|
- [ASP.NET Core Hosting Bundle](https://dotnet.microsoft.com/download/dotnet/8.0) (under Hosting Bundle)
|
|
|
|
### 2. Build
|
|
|
|
```powershell
|
|
cd ScratchingPost
|
|
dotnet restore
|
|
dotnet publish -c Release -o ./publish
|
|
```
|
|
|
|
### 3. Deploy to IIS
|
|
|
|
1. Open **IIS Manager**
|
|
2. Create a new **Site** (e.g. ScratchingPost)
|
|
3. Set the **physical path** to the `publish` folder
|
|
4. Set the **binding** (e.g. port 80, or a hostname like `sign.local`)
|
|
5. Set the **Application Pool** to **No Managed Code** (the Hosting Bundle handles .NET)
|
|
6. Ensure the App Pool identity has **write access** to the publish folder (for SQLite DB and uploads)
|
|
|
|
### 4. First Run
|
|
|
|
The SQLite database (`noticeboard.db`) is created automatically on first start with two demo devices seeded:
|
|
- **Front of House** at `http://server/frontofhouse`
|
|
- **Staff Room** at `http://server/staffroom`
|
|
|
|
### 5. Access
|
|
|
|
| URL | Purpose |
|
|
|-----|---------|
|
|
| `http://server/admin` | Scratching Post admin panel |
|
|
| `http://server/admin/slides` | Manage Meows (slides) |
|
|
| `http://server/admin/devices` | Manage Kittens (devices) |
|
|
| `http://server/frontofhouse` | Front of House display |
|
|
| `http://server/staffroom` | Staff Room display |
|
|
|
|
## Docker Deployment
|
|
|
|
An alternative to IIS — runs anywhere Docker does (Linux, Windows with WSL2 backend, Synology/QNAP NAS, a Raspberry Pi, etc.).
|
|
|
|
### 1. Build and run
|
|
|
|
```bash
|
|
git clone https://gitea.hideawaygaming.com.au/jessikitty/ScratchingPost.git
|
|
cd ScratchingPost
|
|
docker compose up -d --build
|
|
```
|
|
|
|
That's it — the container listens on port **8080**, so open `http://server:8080`.
|
|
|
|
### 2. What the compose file sets up
|
|
|
|
`docker-compose.yml` builds the image from the included `Dockerfile` and wires up two bind-mounted folders next to the compose file so your data survives container rebuilds/updates:
|
|
|
|
| Host path | Container path | Purpose |
|
|
|-----------|-----------------|---------|
|
|
| `./data` | `/data` | `noticeboard.db` (SQLite) |
|
|
| `./uploads` | `/app/wwwroot/uploads` | Images uploaded via TinyMCE, plus uploaded ICS files |
|
|
|
|
Both folders are created automatically on first run.
|
|
|
|
### 3. Configuration
|
|
|
|
Same settings as IIS, just as environment variables in `docker-compose.yml` (nested keys use a double underscore):
|
|
|
|
```yaml
|
|
environment:
|
|
- DataDirectory=/data
|
|
- Admin__Username=admin
|
|
- Admin__Password=ScratchingPost2026!
|
|
- MaxUploadSizeMB=20
|
|
```
|
|
|
|
**Change the admin password before exposing this beyond your local network.**
|
|
|
|
### 4. Updating
|
|
|
|
```bash
|
|
git pull
|
|
docker compose up -d --build
|
|
```
|
|
|
|
The database and uploads persist across rebuilds since they live in the bind-mounted `./data` and `./uploads` folders, not inside the image. On startup the app checks the SQLite schema and adds any new columns it needs (e.g. the `ExpiresAt` column below) automatically — no manual migration step.
|
|
|
|
### 5. Reverse proxy / HTTPS
|
|
|
|
The container serves plain HTTP on 8080. For a public-facing deployment, put it behind a reverse proxy (Caddy, nginx, Traefik) that terminates HTTPS and forwards to `scratchingpost:8080`.
|
|
|
|
### Running IIS and Docker side by side
|
|
|
|
The IIS deployment and the Docker image are built from the same codebase and don't share a database or uploads folder by default — they're independent instances unless you point them at the same SQLite file and uploads directory yourself.
|
|
|
|
## Configuration
|
|
|
|
Edit `appsettings.json`:
|
|
|
|
```json
|
|
{
|
|
"MaxUploadSizeMB": 20
|
|
}
|
|
```
|
|
|
|
## How It Works
|
|
|
|
1. **Create Meows** (slides) — use the WYSIWYG editor for rich content, embed external URLs, or point to an ICS calendar feed
|
|
2. **Create Kittens** (devices) — give each a name, URL slug, and display resolution
|
|
3. **Build PURRs** (playlists) — assign Meows to Kittens with individual durations and drag-and-drop ordering
|
|
4. **Display** — point a browser at `http://server/{slug}` and it runs full-screen with smooth transitions
|
|
|
|
The display page polls for changes every 30 seconds, so updates appear on devices without restarting.
|
|
|
|
## Meow Expiration
|
|
|
|
Any Meow can be given an **Expires** date/time in its editor (under **Scheduling**). Once that time passes:
|
|
|
|
- The Meow is automatically skipped by every Kitten it's assigned to — no manual removal needed
|
|
- It stays fully intact in **Meows** (with a red **Expired** badge) so it can be re-used or have its expiry pushed back later
|
|
- Nothing is deleted — expiry only affects playback, not the stored slide
|
|
|
|
Leave it blank for a Meow that should run indefinitely. This is checked using the server's local clock, so make sure the server's time zone is set correctly.
|
|
|
|
## Display Controls
|
|
|
|
Keyboard shortcuts on display pages (useful during setup):
|
|
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| Right Arrow or Space | Skip to next slide |
|
|
| Left Arrow | Go to previous slide |
|
|
| F or F11 | Toggle fullscreen |
|
|
| C | Toggle cursor visibility |
|
|
|
|
## Data Storage
|
|
|
|
- **Database**: SQLite (`noticeboard.db`) — created automatically, zero config
|
|
- **Uploads**: `wwwroot/uploads/` — images uploaded via TinyMCE or the file upload API
|
|
- On IIS, both need write permissions for the App Pool identity
|
|
- On Docker, both live in bind-mounted host folders (`./data`, `./uploads`) — see [Docker Deployment](#docker-deployment)
|
|
|
|
## TinyMCE Note
|
|
|
|
The WYSIWYG editor uses TinyMCE CDN with `no-api-key`. This shows a small notification banner. For production, register for a free TinyMCE API key at [tiny.cloud](https://www.tiny.cloud/) and replace `no-api-key` in `Views/Slides/Create.cshtml` and `Edit.cshtml`.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
ScratchingPost/
|
|
├── Controllers/ # MVC controllers
|
|
│ ├── AdminController # Dashboard
|
|
│ ├── SlidesController # Meow CRUD
|
|
│ ├── DevicesController # Kitten CRUD + Playlist management
|
|
│ ├── DisplayController # Device display + playlist API
|
|
│ └── ApiController # Image upload, ICS parsing
|
|
├── Data/ # EF Core DbContext + seed data
|
|
├── Models/ # Slide, Device, DeviceSlide
|
|
├── Routing/ # DeviceSlugConstraint
|
|
├── Views/ # Razor views (Admin, Slides, Devices, Display)
|
|
├── wwwroot/ # Static files
|
|
│ ├── css/ # admin.css, display.css
|
|
│ ├── js/ # admin.js, display.js (Sunbeam engine)
|
|
│ └── uploads/ # User-uploaded images
|
|
├── appsettings.json # Configuration
|
|
├── web.config # IIS configuration
|
|
├── Dockerfile # Container build (alternative to IIS)
|
|
├── docker-compose.yml # Container run config (ports, volumes)
|
|
├── .dockerignore
|
|
└── NoticeBoard.csproj # Project file
|
|
```
|
|
|
|
## Version History
|
|
|
|
- **v1.1.0** — Docker deployment (`Dockerfile` + `docker-compose.yml`, configurable data directory, volume-backed DB/uploads), Meow expiration (`ExpiresAt` field, automatic playback filtering, auto-migrating column for existing databases)
|
|
- **v1.0.0** — Initial release: WYSIWYG slides, embed, ICS calendar, device playlists, drag-and-drop ordering, live polling, IIS-ready deployment
|