Skip to content
Merged
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
12 changes: 9 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,16 @@ jobs:
ssh -i ~/.ssh/deploy_key -o BatchMode=yes -p 22 "${user}@${host}" \
"mkdir -p '${server_dir}/cache' && chmod 775 '${server_dir}/cache'"

- name: Smoke test call-api.php
- name: Smoke test production
if: ${{ github.event_name != 'workflow_dispatch' || inputs.dry_run != true }}
run: |
set -euo pipefail
curl -fsS "https://www.pinchards.is/waves/call-api.php" | python3 -c "import json,sys; data=json.load(sys.stdin); assert 'wind' in data"
curl -fsS "https://www.pinchards.is/waves/health.php" | python3 -c "import json,sys; data=json.load(sys.stdin); assert 'status' in data"
base="https://www.pinchards.is/waves"
curl -fsS "${base}/call-api.php" | python3 -c "import json,sys; data=json.load(sys.stdin); assert 'wind' in data"
curl -fsS "${base}/health.php" | python3 -c "import json,sys; data=json.load(sys.stdin); assert 'status' in data"
curl -fsS -o /dev/null "${base}/assets/css/waves.css"
curl -fsS -o /dev/null "${base}/assets/js/waves.js"
curl -fsS -o /dev/null "${base}/assets/images/favicon.svg"
code="$(curl -fsS -o /dev/null -w '%{http_code}' "${base}/wave.php")"
test "${code}" = "301" -o "${code}" = "200"
Comment on lines +132 to +133
echo "Smoke tests passed."
1 change: 1 addition & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
RewriteEngine On

# Legacy entry points (wave.php / wave2.php removed; index.php handles all layouts)
RewriteRule ^wave\.php$ ?layout=wide [R=301,L,QSA]
RewriteRule ^wave2\.php$ ?layout=wide&station=0 [R=301,L,QSA]

Expand Down
67 changes: 29 additions & 38 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,47 @@
## Unreleased

### Added
- `og-image.png` for social link previews
- PHP lint + PHPUnit workflow (`.github/workflows/php.yml`)
- `health.php` monitoring endpoint
- Scheduled production uptime checks (`.github/workflows/uptime.yml`)
- Keyboard orbit controls (arrow keys)
- PHPUnit coverage for `lib/erddap.php`
- Content-Security-Policy header in `.htaccess`
- `wave_period` exposed in API JSON and station readout
- `assets/` tree: `assets/css/`, `assets/js/`, `assets/images/`
- Deploy smoke tests for static asset URLs

### Changed
- Station panel labels: **Wave period** (seconds) and **Wave height** (metres)
- Google Fonts loaded via CSS2 API (`fonts.googleapis.com/css2`)
- Open Graph / Twitter images point to `og-image.png`
- Static files moved out of the site root into `assets/`
- `NOTICE` merged into `LICENSE` (Attributions section)
- README documents project layout and legacy URL handling
- Twitter Card uses `summary` (no social preview image for now)

### Removed
- `wave.php` and `wave2.php` (legacy URLs redirect via `.htaccess` only)
- `og-image.png`, `og-image.svg`, and Open Graph image meta tags
- Standalone `NOTICE` file

## 2026-07-01 — Pass 3

### Added
- PHP lint + PHPUnit CI, `health.php`, uptime workflow
- Keyboard orbit, CSP header, `wave_period` in API JSON

### Changed
- Station labels: wave period (s), wave height (m)
- Google Fonts via CSS2 API

## 2026-07-01 — Pass 2

### Added
- Named ERDDAP column parsing via `columnNames`
- Wind direction support (`wind_dir_avg` → `wind_x` / `wind_y`)
- ERDDAP cache lock to prevent upstream stampedes
- `robots.txt`, `sitemap.xml`, `favicon.svg`, and `og-image.svg`
- `.htaccess` legacy redirects and static asset cache headers
- Touch orbit controls and `prefers-reduced-motion` handling
- Shader compile/link error reporting
- Deploy workflow cache permissions step and `call-api.php` smoke test
- Named ERDDAP columns, wind direction, cache lock
- `robots.txt`, `sitemap.xml`, `favicon.svg`, `.htaccess` redirects
- Touch controls, shader error reporting, deploy hardening

### Changed
- Canonical URLs no longer include `index.php`
- Wide layout CSS no longer overridden by `waves.js` resize inline styles
- `layout-immersive` renamed to `layout-wide-cinematic`
- Station boot config consolidated into `window.STATION`
- Deploy workflow renamed from "Deploy SFTP" to "Deploy"
- Canonical URLs, wide layout fix, `window.STATION`, `layout-wide-cinematic`

### Removed
- Unused vector helpers from `shared.js`
- Redundant `hidden` attribute on the station panel (CSS `station-hidden` only)
- Legacy orbit mode state (`NONE` / `ORBITING`)

### Fixed
- Wide layout canvas positioning
- `<time datetime>` updates during polling
- ERDDAP `NaN` / missing values preserving last good readings
- `call-api.php` `Cache-Control: max-age=10`
- Unused vector helpers, redundant station `hidden` attribute

## 2026-07-01
## 2026-07-01 — Pass 1

### Added
- Initial GitHub import and consolidation pass
- Shared `lib/erddap.php`, single `index.php`, `station-poll.js`
- SEO meta tags, README, LICENSE, and NOTICE
- Single `index.php`, `lib/erddap.php`, `station-poll.js`, SEO meta, LICENSE

### Removed
- jQuery, `ui.js`, and hidden 3D camera UI
- jQuery, `ui.js`, hidden 3D camera UI
17 changes: 17 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ 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.

---

## Attributions

This project bundles a WebGL FFT ocean simulation (`assets/js/simulation.js`,
`assets/js/shared.js`, `assets/js/waves.js`) derived from publicly circulated
WebGL ocean demos that implement Stockham FFT spectrum updates on the GPU. The
implementation lineage is commonly associated with interactive WebGL ocean
experiments built on Jerry Tessendorf's FFT wave model.

Project-specific work includes SmartAtlantic buoy integration, PHP ERDDAP
proxying, layout consolidation, station polling, and pinchards.is deployment.

**Data:** [SmartAtlantic ERDDAP](https://www.smartatlantic.ca/erddap/)

**Fonts:** [Open Sans](https://fonts.google.com/specimen/Open+Sans) via Google Fonts
19 changes: 0 additions & 19 deletions NOTICE

This file was deleted.

135 changes: 67 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,57 @@ The page renders a GPU-accelerated FFT ocean simulation and feeds it with the la

All views are served from `index.php` with query parameters:

| URL | Former page | Description |
|-----|-------------|-------------|
| `/` | `index.php` | Default view with station readout |
| `?layout=wide` | `wave.php` | Wide panoramic canvas |
| `?layout=wide&station=0` | `wave2.php` | Wide cinematic canvas, station panel hidden |
| URL | Description |
|-----|-------------|
| `/waves/` | Default view with station readout |
| `/waves/?layout=wide` | Wide panoramic canvas |
| `/waves/?layout=wide&station=0` | Wide cinematic canvas, station panel hidden |

`wave.php` and `wave2.php` remain as permanent redirects (PHP and `.htaccess`) for old links.
Legacy URLs (`wave.php`, `wave2.php`) redirect via `.htaccess` — no duplicate PHP entry points.

## Project structure

The site root keeps only PHP endpoints and web metadata. Static assets live under `assets/`.

```
index.php Single HTML entry point (layouts via query string)
call-api.php JSON endpoint for the 10s polling loop
health.php Monitoring endpoint (cache + ERDDAP status)
lib/
erddap.php ERDDAP fetch, named columns, cache lock, NaN handling
layout.php Layout flags, canonical URLs, client payload helper
station-poll.js Polls call-api.php; updates readout + simulator
shared.js Shared constants, math helpers, shader utilities
simulation.js WebGL FFT ocean simulator
waves.js Page bootstrap, camera orbit, render loop
waves.css Layout and station panel styles
tests/ PHPUnit tests for lib/erddap.php
cache/ Server-writable ERDDAP cache and lock files
robots.txt Crawler rules
sitemap.xml Public layout URLs
favicon.svg Site icon
og-image.png Social preview image (Open Graph / Twitter)
.htaccess Legacy redirects, CSP, and static asset cache headers
waves/
├── index.php # HTML entry point
├── call-api.php # JSON polling endpoint
├── health.php # Monitoring endpoint
├── lib/
│ ├── erddap.php # ERDDAP fetch, cache, NaN handling
│ └── layout.php # Layout flags, canonical URLs, asset paths
├── assets/
│ ├── css/
│ │ └── waves.css
│ ├── js/
│ │ ├── shared.js # Constants, math, shader helpers
│ │ ├── simulation.js # WebGL FFT ocean simulator
│ │ ├── waves.js # Bootstrap, orbit, render loop
│ │ └── station-poll.js
│ └── images/
│ └── favicon.svg
├── cache/ # Writable ERDDAP cache (gitignored data)
├── tests/ # PHPUnit tests
├── .htaccess # Legacy redirects, CSP, cache headers
├── robots.txt
├── sitemap.xml
├── composer.json
├── phpunit.xml
├── LICENSE # MIT license + attributions
├── CHANGELOG.md
└── README.md
```

### Why this layout

| Layer | Location | Rationale |
|-------|----------|-----------|
| PHP endpoints | Site root | DreamHost serves `index.php` and JSON APIs directly |
| Shared PHP | `lib/` | Reusable server logic, no public URLs |
| CSS / JS / images | `assets/` | Cacheable static files, clear separation |
| Tests | `tests/` | Not deployed; excluded from rsync implicitly by size |
Comment on lines +57 to +62

## Data mapping

ERDDAP columns are mapped by name in `lib/erddap.php`:
Expand All @@ -62,12 +82,10 @@ wind_y = -speed * cos(direction)

Missing or `NaN` readings fall back to the previous cached value when possible.

Responses are cached for 60 seconds in `cache/erddap-latest.json` with a file lock in `cache/erddap.lock` to prevent upstream stampedes.
Responses are cached for 60 seconds in `cache/erddap-latest.json` with a file lock in `cache/erddap.lock`.

## Local development

PHP is required for the ERDDAP proxy and JSON endpoint.

```bash
php -S localhost:8080
```
Expand All @@ -76,8 +94,8 @@ Open [http://localhost:8080/](http://localhost:8080/). The `cache/` directory mu

### Requirements

- PHP 8.0+ with `allow_url_fopen` (or swap in cURL in `lib/erddap.php`)
- A browser with WebGL and `OES_texture_float` / `OES_texture_float_linear`
- PHP 8.0+ with `allow_url_fopen`
- WebGL with `OES_texture_float` and `OES_texture_float_linear`

### Tests

Expand All @@ -86,74 +104,55 @@ composer install
composer exec phpunit
```

GitHub Actions also runs `php -l` on every push/PR (`.github/workflows/php.yml`).
CI runs `php -l` and PHPUnit on every push/PR (`.github/workflows/php.yml`).

## Deploy

On **push to `main`**, `.github/workflows/deploy.yml` rsyncs this repo to `waves/` on DreamHost (same server as [pinchards.is](https://github.com/adamsimms/pinchards.is)).

The workflow also:
On **push to `main`**, `.github/workflows/deploy.yml` rsyncs to `waves/` on DreamHost.

- Ensures `cache/` exists and is writable (`chmod 775`)
- Smoke-tests `call-api.php` and `health.php` after deploy

### Repository secrets

Reuse the DreamHost deploy secrets from pinchards.is:
The workflow ensures `cache/` is writable and smoke-tests `call-api.php` and `health.php`.

| Secret | Notes |
|--------|--------|
| `FTP_SERVER` | SSH hostname |
| `FTP_USERNAME` | Shell user |
| `FTP_SERVER_DIR` | Site root, e.g. `/home/USER/pinchards.is` (workflow appends `/waves`) |
| `SSH_DEPLOY_KEY` | ed25519 private key (base64-encoded single line) |
| `FTP_SERVER_DIR` | Site root (workflow appends `/waves`) |
| `SSH_DEPLOY_KEY` | ed25519 private key |

Use **Actions → Deploy → Run workflow** with `dry_run: true` to preview changes.
Use **Actions → Deploy → Run workflow** with `dry_run: true` to preview.

## Monitoring

| Endpoint | Purpose |
|----------|---------|
| `health.php` | JSON status: cache writable, latest station fetch |
| `call-api.php` | Live buoy JSON used by the page |

`.github/workflows/uptime.yml` curls production every 30 minutes. You can also point [UptimeRobot](https://uptimerobot.com/) or similar at:
| `health.php` | Cache writable + latest ERDDAP fetch |
| `call-api.php` | Live buoy JSON |

- `https://www.pinchards.is/waves/health.php`
- `https://www.pinchards.is/waves/call-api.php`
`.github/workflows/uptime.yml` checks production every 30 minutes.

## SEO

`index.php` includes:
- Descriptive title and meta description
- Canonical URLs without `index.php`
- Open Graph and Twitter Card tags (`summary` card; no `og:image` until a preview asset is added under `assets/images/`)
- JSON-LD `WebApplication` with `dateModified`
- `robots.txt`, `sitemap.xml`, favicon at `assets/images/favicon.svg`

- Descriptive `<title>` and meta description
- Clean canonical URLs (`https://www.pinchards.is/waves/`, no `index.php`)
- Open Graph and Twitter Card tags with `og-image.png`
- JSON-LD `WebApplication` structured data with `dateModified`
- Semantic HTML (`main`, `section`, `dl` metrics, `time` element)
- `robots.txt`, `sitemap.xml`, and `favicon.svg`

Typography is loaded from [Google Fonts](https://fonts.google.com/specimen/Open+Sans) (Open Sans).
Typography: [Open Sans](https://fonts.google.com/specimen/Open+Sans) via Google Fonts.

## Security

`.htaccess` sets a Content-Security-Policy allowing:

- Same-origin scripts and styles
- Google Fonts (`fonts.googleapis.com`, `fonts.gstatic.com`)
- Google Analytics / Tag Manager
`.htaccess` sets Content-Security-Policy for same-origin assets, Google Fonts, and Google Analytics.

## Accessibility

- Touch orbit controls on mobile (`touch-action: none` on the overlay)
- Keyboard orbit with arrow keys (focus the overlay)
- `prefers-reduced-motion` lowers orbit sensitivity and skips live simulator retuning during polls
- Overlay is exposed to assistive tech with an orbit label
- Touch and keyboard (arrow keys) orbit controls
- `prefers-reduced-motion` lowers sensitivity and skips live simulator retuning during polls

## License

See [LICENSE](LICENSE) and [NOTICE](NOTICE).
[LICENSE](LICENSE) — MIT license with attributions for the ocean simulation, ERDDAP data source, and fonts.

## Changelog

See [CHANGELOG.md](CHANGELOG.md).
[CHANGELOG.md](CHANGELOG.md)
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading