Openstatus www.openstatus.dev

Containerize core services (#1585)

* feat: Enable standalone builds for Next.js configs

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Improve Docker setup and configuration

- Enhance Dofigen configs with proper labels and health checks
- Improve Dockerfiles for server, workflows, and private-location
- Restructure docker-compose.yaml

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Add Dofigen config for private-location Docker image

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Resolve Next.js fetch type error in TRPC server

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Add Docker setup for Next.js apps
- Add Dofigen and Dockerfile configs for status-page and dashboard Next.js applications
- Update docker-compose to build all services

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Add service communication configurations

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Improve docker compose setup

- Add complete docker-compose.yaml for services
- Fix healthchecks across containers
- Fix Next.js standalone builds: use pnpm symlinks and cache mounts
- Fix auth issues: add AUTH_TRUST_HOST and remove EXTERNAL_API_URL
- Fix workflows: create /app/data before USER switch, add ca-certs for HTTPS
- Document dofigen workarounds for non-deterministic issues

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Validate domain slug and add client-side error handling

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Remove EXTERNAL_API_URL from status-page app

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Exclude localhost from subdomain detection

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Optimize Docker setup and add documentation

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* ci: apply automated fixes

* use turbo build

* fix: Add required env vars for Docker builds

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* clean up tb

* clean up tb

* feat: Install curl for health checks

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* docs: Add database migration and seeding steps to DOCKER.md

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Add support for magic link authentication

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Remove EXTERNAL_API_URL from dashboard app

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: General improvements

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* feat: Add configurable Tinybird URL

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* ci: apply automated fixes

* tb endpoint

* fix: Reuse existing 404 component

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* small stuff

* feat: Conditionally enable standalone output for self-hosted deployments

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

* fix: Revert error handling on status page

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>

---------

Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Thibault Le Ouay <thibaultleouay@gmail.com>

authored by

Ramtin Mesgari
autofix-ci[bot]
Thibault Le Ouay
and committed by
GitHub
b4577f2e 403b20f5

+2475 -834
+99 -5
.dockerignore
··· 1 - **/.dockerignore 1 + # Dependencies 2 2 **/node_modules 3 - **/npm-debug.log 4 - **/README.md 3 + node_modules 4 + **/.pnpm-store 5 + .pnpm-store 6 + 7 + # Build outputs 5 8 **/.next 9 + **/.turbo 10 + **/dist 11 + **/build 12 + **/.nuxt 13 + **/.output 14 + **/.cache 15 + 16 + # Environment files 17 + **/.env 18 + **/.env.* 19 + .env 20 + .env.* 21 + !.env.docker.example 22 + 23 + # Development files 24 + **/.vscode 25 + **/.idea 26 + **/.DS_Store 27 + .DS_Store 28 + **/*.log 29 + **/npm-debug.log 30 + **/yarn-debug.log 31 + **/yarn-error.log 32 + **/pnpm-debug.log 33 + 34 + # Testing 35 + **/coverage 36 + **/.nyc_output 37 + **/test-results 38 + **/__tests__ 39 + **/tests 40 + **/*.test.ts 41 + **/*.test.tsx 42 + **/*.test.js 43 + **/*.spec.ts 44 + **/*.spec.tsx 45 + **/*.spec.js 46 + 47 + # Git 6 48 **/.git 7 - node_modules 8 - openstatus.db 49 + **/.gitignore 50 + **/.gitattributes 51 + .git 52 + .gitignore 53 + 54 + # Documentation 55 + **/README.md 56 + **/CHANGELOG.md 57 + **/LICENSE 58 + **/*.md 59 + !packages/**/README.md 60 + 61 + # CI/CD 62 + **/.github 63 + .github 64 + **/.gitlab 65 + **/.circleci 66 + **/.drone.yml 67 + **/.travis.yml 68 + 69 + # Docker 70 + **/.dockerignore 71 + **/Dockerfile 72 + **/docker-compose*.yml 73 + **/docker-compose*.yaml 74 + .dockerignore 75 + docker-compose*.yml 76 + docker-compose*.yaml 77 + 78 + # Database files 79 + openstatus.db 80 + **/*.db 81 + **/*.db-shm 82 + **/*.db-wal 83 + 84 + # Temporary files 85 + **/tmp 86 + **/temp 87 + **/.temp 88 + **/.tmp 89 + 90 + # IDE 91 + **/.vscode 92 + **/.idea 93 + **/.fleet 94 + 95 + # OS 96 + .DS_Store 97 + **/Thumbs.db 98 + 99 + # Other build artifacts 100 + **/.contentlayer 101 + **/public/build 102 + **/.svelte-kit
+153
.env.docker.example
··· 1 + # ============================================================================ 2 + # Docker Environment Configuration (Example) 3 + # ============================================================================ 4 + # Copy this file to .env.docker and replace with your actual values 5 + # 6 + # NOTE: Build-time placeholders are set in each service's dofigen.yml file. 7 + # Values in this file override those placeholders at runtime via docker compose. 8 + # Required values are marked with [REQUIRED] 9 + 10 + # DATABASE 11 + # ============================================================================ 12 + # LibSQL database connection - points to the libsql container 13 + DATABASE_URL=http://libsql:8080 14 + DATABASE_AUTH_TOKEN= 15 + 16 + # ClickHouse (optional - for advanced analytics) 17 + CLICKHOUSE_URL= 18 + CLICKHOUSE_USERNAME= 19 + CLICKHOUSE_PASSWORD= 20 + 21 + # REDIS & QUEUE 22 + # ============================================================================ 23 + # Redis (optional) - for caching 24 + UPSTASH_REDIS_REST_URL=http://localhost:6379 25 + UPSTASH_REDIS_REST_TOKEN=placeholder 26 + 27 + # QStash (optional - for background jobs) 28 + QSTASH_CURRENT_SIGNING_KEY= 29 + QSTASH_NEXT_SIGNING_KEY= 30 + QSTASH_TOKEN= 31 + QSTASH_URL=https://qstash.upstash.io/v1/publish/ 32 + 33 + # ANALYTICS 34 + # ============================================================================ 35 + # Tinybird (optional - for monitor analytics and charts) 36 + # Leave empty to disable analytics features 37 + # Get from: https://www.tinybird.co 38 + TINY_BIRD_API_KEY= 39 + 40 + # Tinybird URL (optional - defaults to cloud API) 41 + # For local Tinybird container, set to: http://tinybird-local:7181 42 + TINYBIRD_URL= 43 + 44 + # EMAIL 45 + # ============================================================================ 46 + # [REQUIRED] Resend API key for sending magic link emails 47 + # Get from: https://resend.com 48 + RESEND_API_KEY=re_your_resend_api_key_here 49 + 50 + # AUTHENTICATION 51 + # ============================================================================ 52 + # [REQUIRED] NextAuth secret - generate with: openssl rand -base64 32 53 + AUTH_SECRET=your-random-secret-here-min-32-chars 54 + 55 + # [REQUIRED] Self-hosted mode - enables magic link authentication 56 + # Set to "true" to allow email login without OAuth 57 + SELF_HOST=true 58 + 59 + # GitHub OAuth (optional) 60 + # Get from: https://github.com/settings/developers 61 + AUTH_GITHUB_ID= 62 + AUTH_GITHUB_SECRET= 63 + 64 + # Google OAuth (optional) 65 + # Get from: https://console.cloud.google.com 66 + AUTH_GOOGLE_ID= 67 + AUTH_GOOGLE_SECRET= 68 + 69 + # GOOGLE CLOUD 70 + # ============================================================================ 71 + # Google Cloud Platform (optional - for scheduled tasks) 72 + GCP_PROJECT_ID= 73 + GCP_LOCATION= 74 + GCP_CLIENT_EMAIL= 75 + GCP_PRIVATE_KEY= 76 + 77 + # Cron secret for scheduled jobs 78 + CRON_SECRET=your-random-cron-secret 79 + 80 + # API KEYS 81 + # ============================================================================ 82 + # Unkey (optional - for API key management) 83 + # Get from: https://unkey.dev 84 + UNKEY_API_ID= 85 + UNKEY_TOKEN= 86 + 87 + # Super admin token for privileged operations 88 + SUPER_ADMIN_TOKEN= 89 + 90 + # Server region identifier 91 + FLY_REGION=self-hosted 92 + 93 + # STRIPE 94 + # ============================================================================ 95 + # Stripe (optional - for payments) 96 + # Get from: https://stripe.com/docs/keys 97 + STRIPE_SECRET_KEY= 98 + STRIPE_WEBHOOK_SECRET_KEY= 99 + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= 100 + 101 + # VERCEL 102 + # ============================================================================ 103 + # Vercel (optional - for custom domains) 104 + # Get from: https://vercel.com 105 + PROJECT_ID_VERCEL= 106 + TEAM_ID_VERCEL= 107 + VERCEL_AUTH_BEARER_TOKEN= 108 + 109 + # Vercel Blob storage (optional) 110 + BLOB_READ_WRITE_TOKEN= 111 + 112 + # OBSERVABILITY 113 + # ============================================================================ 114 + # Sentry (optional - error tracking) 115 + # Get from: https://sentry.io 116 + NEXT_PUBLIC_SENTRY_DSN= 117 + SENTRY_AUTH_TOKEN= 118 + 119 + # OpenPanel (optional - analytics) 120 + # Get from: https://openpanel.dev 121 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID= 122 + OPENPANEL_CLIENT_SECRET= 123 + 124 + # PagerDuty (optional - alerting) 125 + PAGERDUTY_APP_ID= 126 + 127 + # Slack webhook (optional) 128 + SLACK_SUPPORT_WEBHOOK_URL= 129 + 130 + # SERVICE CONFIGURATION 131 + # ============================================================================ 132 + NODE_ENV=production 133 + 134 + # [REQUIRED] Public URL for the application 135 + NEXT_PUBLIC_URL=http://localhost:3002 136 + 137 + # Screenshot service (optional) 138 + SCREENSHOT_SERVICE_URL= 139 + 140 + # External services 141 + OPENSTATUS_INGEST_URL=https://openstatus-private-location.fly.dev 142 + 143 + # DEVELOPMENT & TESTING 144 + # ============================================================================ 145 + # Turbo build mode 146 + TURBO_ENV_MODE=loose 147 + 148 + # Playground API keys (optional) 149 + PLAYGROUND_UNKEY_API_KEY= 150 + 151 + # Workspace settings (optional) 152 + WORKSPACES_LOOKBACK_30= 153 + WORKSPACES_HIDE_URL=
+276
DOCKER.md
··· 1 + # Docker Setup Guide 2 + 3 + Complete guide for running OpenStatus with Docker 4 + 5 + ## Quick Start 6 + 7 + ```bash 8 + # 1. Copy environment file 9 + cp .env.docker.example .env.docker 10 + 11 + # 2. Configure required variables (see Configuration section) 12 + vim .env.docker 13 + 14 + # 3. Build and start services 15 + export DOCKER_BUILDKIT=1 16 + docker compose up -d 17 + 18 + # 4. Check service health 19 + docker compose ps 20 + 21 + # 5. Run database migrations (required) 22 + cd packages/db 23 + pnpm migrate 24 + 25 + # 6. Deploy Tinybird local 26 + cd packages/tinybird 27 + tb --local deploy 28 + 29 + # 7. Seed database with test data (optional) 30 + cd packages/db 31 + pnpm seed 32 + 33 + 34 + # 8. Access the application 35 + open http://localhost:3002 # Dashboard 36 + open http://localhost:3003 # Status Pages 37 + ``` 38 + 39 + ## Cleanup 40 + 41 + ```bash 42 + # Remove stopped containers 43 + docker compose down 44 + 45 + # Remove volumes 46 + docker compose down -v 47 + 48 + # Clean build cache 49 + docker builder prune 50 + ``` 51 + 52 + ## Services 53 + 54 + | Service | Port | Purpose | 55 + |---------|------|---------| 56 + | workflows | 3000 | Background jobs | 57 + | server | 3001 | API backend (tRPC) | 58 + | dashboard | 3002 | Admin interface | 59 + | status-page | 3003 | Public status pages | 60 + | private-location | 8081 | Monitoring agent | 61 + | libsql | 8080 | Database (HTTP) | 62 + | libsql | 5001 | Database (gRPC) | 63 + | tinybird-local | 7181 | Analytics | 64 + 65 + 66 + ## Architecture 67 + 68 + ``` 69 + ┌─────────────┐ ┌─────────────┐ 70 + │ Dashboard │────▶│ Server │ 71 + │ (Next.js) │ │ (Bun) │ 72 + └─────────────┘ └─────────────┘ 73 + │ │ 74 + ▼ ▼ 75 + ┌─────────────┐ ┌─────────────┐ 76 + │ Status Page │ │ Workflows │ 77 + │ (Next.js) │ │ (Bun) │ 78 + └─────────────┘ └─────────────┘ 79 + │ │ 80 + └────────┬───────────┘ 81 + 82 + ┌─────────────┐ 83 + │ LibSQL │ 84 + │ (Database) │ 85 + └─────────────┘ 86 + ``` 87 + 88 + ## Database Setup 89 + 90 + The LibSQL container starts with an empty database. You must run migrations before using the application: 91 + 92 + ```bash 93 + cd packages/db 94 + pnpm migrate 95 + ``` 96 + 97 + ### Seeding Test Data (Optional) 98 + 99 + For development, you can populate the database with sample data: 100 + 101 + ```bash 102 + cd packages/db 103 + pnpm seed 104 + ``` 105 + 106 + This creates: 107 + - 3 workspaces (`love-openstatus`, `test2`, `test3`) 108 + - 5 sample monitors and 1 status page 109 + - Test user account: `ping@openstatus.dev` 110 + - Sample incidents, status reports, and maintenance windows 111 + 112 + **Accessing Seeded Data:** 113 + 114 + To view the seeded data in the dashboard, you must log in using the seeded test email: 115 + 116 + 1. Navigate to http://localhost:3002/login 117 + 2. Use magic link authentication with email: `ping@openstatus.dev` 118 + 3. Check your console/logs for the magic link 119 + 4. After logging in, you'll be in the `love-openstatus` workspace with all seeded data 120 + 121 + **If you use a different email address**, the system will create a new empty workspace for you. To access seeded data with a different account: 122 + 123 + 1. Add your user to the seeded workspace using SQL: 124 + ```bash 125 + # First, find your user_id 126 + curl -X POST http://localhost:8080/ -H "Content-Type: application/json" \ 127 + -d '{"statements":["SELECT id, email FROM user"]}' 128 + 129 + # Then add association (replace USER_ID with your id) 130 + curl -X POST http://localhost:8080/ -H "Content-Type: application/json" \ 131 + -d '{"statements":["INSERT INTO users_to_workspaces (user_id, workspace_id, role) VALUES (USER_ID, 1, '\''owner'\'')"]}' 132 + ``` 133 + 134 + 2. Switch to the `love-openstatus` workspace using the workspace switcher in the dashboard sidebar 135 + 136 + ## Tinybird Setup (Optional) 137 + 138 + Tinybird is used for analytics and monitoring metrics. The application will work without it, but analytics features will be unavailable. 139 + 140 + If you want to enable analytics, you can: 141 + 1. Use Tinybird Cloud and configure `TINY_BIRD_API_KEY` in `.env.docker` 142 + 2. Manually configure Tinybird Local (requires additional setup beyond this guide) 143 + 144 + ## Configuration 145 + 146 + ### Required Environment Variables 147 + 148 + Edit `.env.docker` and set: 149 + 150 + ```bash 151 + # Authentication 152 + AUTH_SECRET=your-secret-here 153 + 154 + # Database 155 + DATABASE_URL=http://libsql:8080 156 + DATABASE_AUTH_TOKEN=basic:token 157 + 158 + # Email 159 + RESEND_API_KEY=test 160 + ``` 161 + 162 + ### Optional Services 163 + 164 + Configure these for full functionality: 165 + 166 + ```bash 167 + # Redis 168 + UPSTASH_REDIS_REST_URL= 169 + UPSTASH_REDIS_REST_TOKEN= 170 + 171 + # Analytics 172 + TINY_BIRD_API_KEY= 173 + 174 + # OAuth providers 175 + AUTH_GITHUB_ID= 176 + AUTH_GITHUB_SECRET= 177 + AUTH_GOOGLE_ID= 178 + AUTH_GOOGLE_SECRET= 179 + ``` 180 + 181 + See [.env.docker.example](.env.docker.example) for complete list. 182 + 183 + ## Development Workflow 184 + 185 + ### Common Commands 186 + 187 + ```bash 188 + # View logs 189 + docker compose logs -f [service-name] 190 + 191 + # Restart service 192 + docker compose restart [service-name] 193 + 194 + # Rebuild after code changes 195 + docker compose up -d --build [service-name] 196 + 197 + # Stop all services 198 + docker compose down 199 + 200 + # Reset database (removes all data) 201 + docker compose down -v 202 + # After resetting, re-run migrations: 203 + # cd packages/db && pnpm migrate 204 + ``` 205 + 206 + ### Authentication 207 + 208 + **Magic Link**: 209 + 210 + Set `SELF_HOST=true` in `.env.docker` to enable email-based magic link authentication. This allows users to sign in without configuring OAuth providers. 211 + 212 + **OAuth Providers**: 213 + 214 + Configure GitHub/Google OAuth credentials in `.env.docker` and set up callback URLs: 215 + - GitHub: `http://localhost:3002/api/auth/callback/github` 216 + - Google: `http://localhost:3002/api/auth/callback/google` 217 + 218 + ### Creating Status Pages 219 + 220 + **Via Dashboard (Recommended)**: 221 + 1. Login to http://localhost:3002 222 + 2. Create a workspace 223 + 3. Create a status page with a slug 224 + 4. Access at http://localhost:3003/[slug] 225 + 226 + **Via Database (Testing)**: 227 + ```bash 228 + # Insert test data 229 + curl -s http://localhost:8080/v2/pipeline \ 230 + -H 'Content-Type: application/json' \ 231 + --data-raw '{ 232 + "requests":[{ 233 + "type":"execute", 234 + "stmt":{ 235 + "sql":"INSERT INTO workspace (id, slug, name) VALUES (1, '\''test'\'', '\''Test Workspace'\'');" 236 + } 237 + }] 238 + }' 239 + ``` 240 + 241 + ### Resource Limits 242 + 243 + Add to `docker-compose.yaml`: 244 + 245 + ```yaml 246 + services: 247 + dashboard: 248 + deploy: 249 + resources: 250 + limits: 251 + cpus: '1.0' 252 + memory: 1G 253 + reservations: 254 + cpus: '0.5' 255 + memory: 512M 256 + ``` 257 + 258 + ## Monitoring 259 + 260 + ### Health Checks 261 + 262 + All services have automated health checks: 263 + 264 + ```bash 265 + # View health status 266 + docker compose ps 267 + 268 + # Inspect specific service 269 + docker inspect openstatus-dashboard --format='{{.State.Health.Status}}' 270 + ``` 271 + 272 + ## Getting Help 273 + 274 + - **Documentation**: [docs.openstatus.dev](https://docs.openstatus.dev) 275 + - **Discord**: [openstatus.dev/discord](https://www.openstatus.dev/discord) 276 + - **GitHub Issues**: [github.com/openstatusHQ/openstatus/issues](https://github.com/openstatusHQ/openstatus/issues)
+21 -3
README.md
··· 79 79 80 80 ## Getting Started 🚀 81 81 82 + ### With Docker (Recommended) 83 + 84 + The fastest way to get started for both development and self-hosting: 85 + 86 + ```sh 87 + # 1. Copy environment file 88 + cp .env.docker.example .env.docker 89 + 90 + # 2. Start all services 91 + docker compose up -d 92 + 93 + # 3. Access the application 94 + open http://localhost:3002 # Dashboard 95 + open http://localhost:3003 # Status Pages 96 + ``` 97 + 98 + 📖 **Full guide**: [DOCKER.md](DOCKER.md) | **Production deployment**: [DOCKER_PRODUCTION.md](DOCKER_PRODUCTION.md) 99 + 82 100 ### With Devbox 83 101 84 102 You can use [Devbox](https://www.jetify.com/devbox/) and get started with the following commands: ··· 92 110 devbox services up 93 111 ``` 94 112 95 - Alternatively, follow the instructions below. 113 + ### Manual Setup 96 114 97 - ### Requirements 115 + #### Requirements 98 116 99 117 - [Node.js](https://nodejs.org/en/) >= 20.0.0 100 118 - [pnpm](https://pnpm.io/) >= 8.6.2 101 119 - [Bun](https://bun.sh/) 102 120 - [Turso CLI](https://docs.turso.tech/quickstart) 103 121 104 - ### Setup 122 + #### Setup 105 123 106 124 1. Clone the repository 107 125
+3 -3
apps/checker/cmd/private/main.go
··· 66 66 return fallback 67 67 } 68 68 69 - // UpdateMonitors fetches the latest monitors and starts/stops jobs as needed 70 - 71 69 func getClient(apiKey string) v1.PrivateLocationServiceClient { 70 + ingestUrl := getEnv("OPENSTATUS_INGEST_URL", "https://openstatus-private-location.fly.dev") 71 + 72 72 client := v1.NewPrivateLocationServiceClient( 73 73 http.DefaultClient, 74 - "https://openstatus-private-location.fly.dev", 74 + ingestUrl, 75 75 connect.WithHTTPGet(), 76 76 connect.WithInterceptors(NewAuthInterceptor(apiKey)), 77 77 )
+2 -4
apps/checker/pkg/scheduler/scheduler.go
··· 29 29 mu sync.Mutex 30 30 } 31 31 32 + // UpdateMonitors fetches the latest monitors and starts/stops jobs as needed 32 33 func (mm *MonitorManager) UpdateMonitors(ctx context.Context) { 33 34 res, err := mm.Client.Monitors(ctx, &connect.Request[v1.MonitorsRequest]{}) 34 35 if err != nil { ··· 99 100 } 100 101 101 102 } 102 - 103 - 104 103 105 104 // TCP monitors: start jobs for new monitors 106 105 for _, m := range res.Msg.TcpMonitors { ··· 175 174 } 176 175 resp, ingestErr := mm.Client.IngestDNS(ctx.Context, &connect.Request[v1.IngestDNSRequest]{ 177 176 Msg: &v1.IngestDNSRequest{ 178 - MonitorId: monitor.Id, 177 + MonitorId: monitor.Id, 179 178 180 179 // Id: data.ID, 181 180 // Uri: monitor.Uri, ··· 205 204 log.Printf("Started TCP monitoring job for %s (%s)", m.Id, m.Uri) 206 205 } 207 206 } 208 - 209 207 210 208 for id := range mm.Scheduler.Tasks() { 211 209 if _, stillExists := currentIDs[id]; !stillExists {
+12 -2
apps/checker/pkg/tinybird/client.go
··· 7 7 "fmt" 8 8 "net/http" 9 9 "net/url" 10 + "os" 10 11 11 12 "github.com/rs/zerolog/log" 12 13 ) 13 14 14 - const baseURL = "https://api.tinybird.co/v0/events" 15 + func getBaseURL() string { 16 + // Use local Tinybird container if available (Docker/self-hosted) 17 + // https://www.tinybird.co/docs/api-reference 18 + if tinybirdURL := os.Getenv("TINYBIRD_URL"); tinybirdURL != "" { 19 + return tinybirdURL + "/v0/events" 20 + } 21 + return "https://api.tinybird.co/v0/events" 22 + } 15 23 16 24 type Client interface { 17 25 SendEvent(ctx context.Context, event any, dataSourceName string) error ··· 20 28 type client struct { 21 29 httpClient *http.Client 22 30 apiKey string 31 + baseURL string 23 32 } 24 33 25 34 func NewClient(httpClient *http.Client, apiKey string) Client { 26 35 return client{ 27 36 httpClient: httpClient, 28 37 apiKey: apiKey, 38 + baseURL: getBaseURL(), 29 39 } 30 40 } 31 41 32 42 func (c client) SendEvent(ctx context.Context, event any, dataSourceName string) error { 33 - requestURL, err := url.Parse(baseURL) 43 + requestURL, err := url.Parse(c.baseURL) 34 44 if err != nil { 35 45 log.Ctx(ctx).Error().Err(err).Msg("unable to parse url") 36 46 return fmt.Errorf("unable to parse url: %w", err)
+3
apps/dashboard/.dockerignore
··· 1 + # This file is generated by Dofigen v2.5.1 2 + # See https://github.com/lenra-io/dofigen 3 +
+1 -1
apps/dashboard/.env.example
··· 50 50 GCP_PRIVATE_KEY= 51 51 CRON_SECRET= 52 52 53 - EXTERNAL_API_URL= 53 + VERCEL_URL=https://app.openstatus.dev 54 54 55 55 PLAYGROUND_UNKEY_API_KEY= 56 56
+83
apps/dashboard/Dockerfile
··· 1 + # syntax=docker/dockerfile:1.11 2 + # This file is generated by Dofigen v2.5.1 3 + # See https://github.com/lenra-io/dofigen 4 + 5 + # builder 6 + FROM node@sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 AS builder 7 + LABEL \ 8 + org.opencontainers.image.base.digest="sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2" \ 9 + org.opencontainers.image.base.name="docker.io/node:24-slim" 10 + ENV \ 11 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID="test" \ 12 + TINY_BIRD_API_KEY="test" \ 13 + DATABASE_AUTH_TOKEN="test" \ 14 + PNPM_HOME="/pnpm" \ 15 + VERCEL_AUTH_BEARER_TOKEN="test" \ 16 + OPENPANEL_CLIENT_SECRET="test" \ 17 + RESEND_API_KEY="test" \ 18 + AUTH_SECRET="build-time-placeholder-min-32-chars-long" \ 19 + STRIPE_SECRET_KEY="test" \ 20 + TEAM_ID_VERCEL="test" \ 21 + CRON_SECRET="test" \ 22 + PATH="$PNPM_HOME:$PATH" \ 23 + UPSTASH_REDIS_REST_TOKEN="test" \ 24 + UNKEY_API_ID="test" \ 25 + DATABASE_URL="http://libsql:8080" \ 26 + NEXT_PUBLIC_URL="http://localhost:3002" \ 27 + NODE_ENV="production" \ 28 + UPSTASH_REDIS_REST_URL="test" \ 29 + UNKEY_TOKEN="test" \ 30 + PROJECT_ID_VERCEL="test" 31 + WORKDIR /app 32 + COPY \ 33 + --link \ 34 + "." "/app/" 35 + RUN <<EOF 36 + corepack enable 37 + pnpm install --frozen-lockfile 38 + pnpm turbo run build --filter=@openstatus/dashboard 39 + EOF 40 + 41 + # runtime 42 + FROM node@sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 AS runtime 43 + LABEL \ 44 + io.dofigen.version="2.5.1" \ 45 + org.opencontainers.image.base.digest="sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2" \ 46 + org.opencontainers.image.base.name="docker.io/node:24-slim" 47 + WORKDIR /app/apps/dashboard 48 + COPY \ 49 + --from=builder \ 50 + --chown=1000:1000 \ 51 + --chmod=555 \ 52 + --link \ 53 + "/app/apps/dashboard/.next/standalone/apps/dashboard/" "./" 54 + COPY \ 55 + --from=builder \ 56 + --chown=1000:1000 \ 57 + --link \ 58 + "/app/node_modules/" "/app/node_modules/" 59 + COPY \ 60 + --from=builder \ 61 + --chown=1000:1000 \ 62 + --link \ 63 + "/app/apps/dashboard/.next/static/" "./.next/static/" 64 + COPY \ 65 + --from=builder \ 66 + --chown=1000:1000 \ 67 + --link \ 68 + "/app/apps/dashboard/public/" "./public/" 69 + USER 0:0 70 + RUN <<EOF 71 + apt-get update 72 + apt-get install -y --no-install-recommends curl 73 + rm -rf /var/lib/apt/lists/* 74 + EOF 75 + USER 1000:1000 76 + EXPOSE 3000 77 + HEALTHCHECK \ 78 + --interval=30s \ 79 + --timeout=10s \ 80 + --start-period=45s \ 81 + --retries=3 \ 82 + CMD curl -f http://localhost:3000/ || exit 1 83 + CMD ["node", "server.js"]
+177
apps/dashboard/dofigen.lock
··· 1 + effective: | 2 + builders: 3 + builder: 4 + fromImage: 5 + path: node 6 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 7 + label: 8 + org.opencontainers.image.base.name: docker.io/node:24-slim 9 + org.opencontainers.image.base.digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 10 + workdir: /app 11 + env: 12 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 13 + TINY_BIRD_API_KEY: test 14 + DATABASE_AUTH_TOKEN: test 15 + PNPM_HOME: /pnpm 16 + VERCEL_AUTH_BEARER_TOKEN: test 17 + OPENPANEL_CLIENT_SECRET: test 18 + RESEND_API_KEY: test 19 + AUTH_SECRET: build-time-placeholder-min-32-chars-long 20 + STRIPE_SECRET_KEY: test 21 + TEAM_ID_VERCEL: test 22 + CRON_SECRET: test 23 + PATH: $PNPM_HOME:$PATH 24 + UPSTASH_REDIS_REST_TOKEN: test 25 + UNKEY_API_ID: test 26 + DATABASE_URL: http://libsql:8080 27 + NEXT_PUBLIC_URL: http://localhost:3002 28 + NODE_ENV: production 29 + UPSTASH_REDIS_REST_URL: test 30 + UNKEY_TOKEN: test 31 + PROJECT_ID_VERCEL: test 32 + copy: 33 + - paths: 34 + - . 35 + target: /app/ 36 + run: 37 + - corepack enable 38 + - pnpm install --frozen-lockfile 39 + - pnpm turbo run build --filter=@openstatus/dashboard 40 + fromImage: 41 + path: node 42 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 43 + label: 44 + org.opencontainers.image.base.digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 45 + io.dofigen.version: 2.5.1 46 + org.opencontainers.image.base.name: docker.io/node:24-slim 47 + user: 48 + user: '1000' 49 + group: '1000' 50 + workdir: /app/apps/dashboard 51 + copy: 52 + - fromBuilder: builder 53 + paths: 54 + - /app/apps/dashboard/.next/standalone/apps/dashboard/ 55 + target: ./ 56 + chmod: '555' 57 + - fromBuilder: builder 58 + paths: 59 + - /app/node_modules/ 60 + target: /app/node_modules/ 61 + - fromBuilder: builder 62 + paths: 63 + - /app/apps/dashboard/.next/static/ 64 + target: ./.next/static/ 65 + - fromBuilder: builder 66 + paths: 67 + - /app/apps/dashboard/public/ 68 + target: ./public/ 69 + root: 70 + run: 71 + - apt-get update 72 + - apt-get install -y --no-install-recommends curl 73 + - rm -rf /var/lib/apt/lists/* 74 + cmd: 75 + - node 76 + - server.js 77 + expose: 78 + - port: 3000 79 + healthcheck: 80 + cmd: curl -f http://localhost:3000/ || exit 1 81 + interval: 30s 82 + timeout: 10s 83 + start: 45s 84 + retries: 3 85 + images: 86 + docker.io: 87 + library: 88 + node: 89 + 24-slim: 90 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 91 + resources: 92 + dofigen.yml: 93 + hash: dbe530c6d76da88f16d50c0f1932e9f5a95417c94ea58e4ddf72a0c81c786412 94 + content: | 95 + builders: 96 + # Stage 1: Next.js build with Node.js 97 + builder: 98 + fromImage: node:24-slim 99 + workdir: /app 100 + copy: 101 + - . /app/ 102 + env: 103 + NODE_ENV: production 104 + PNPM_HOME: /pnpm 105 + PATH: $PNPM_HOME:$PATH 106 + # Build-time environment variables (placeholder values, overwritten by .env.docker at runtime) 107 + DATABASE_URL: http://libsql:8080 108 + DATABASE_AUTH_TOKEN: test 109 + RESEND_API_KEY: test 110 + UPSTASH_REDIS_REST_URL: test 111 + UPSTASH_REDIS_REST_TOKEN: test 112 + STRIPE_SECRET_KEY: test 113 + PROJECT_ID_VERCEL: test 114 + TEAM_ID_VERCEL: test 115 + VERCEL_AUTH_BEARER_TOKEN: test 116 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 117 + OPENPANEL_CLIENT_SECRET: test 118 + TINY_BIRD_API_KEY: test 119 + CRON_SECRET: test 120 + UNKEY_TOKEN: test 121 + UNKEY_API_ID: test 122 + AUTH_SECRET: build-time-placeholder-min-32-chars-long 123 + NEXT_PUBLIC_URL: http://localhost:3002 124 + run: 125 + - corepack enable 126 + - pnpm install --frozen-lockfile 127 + - pnpm turbo run build --filter=@openstatus/dashboard 128 + 129 + # Runtime stage 130 + fromImage: node:24-slim 131 + workdir: /app/apps/dashboard 132 + 133 + # Copy artifacts from builder 134 + copy: 135 + # Copy Next.js standalone output 136 + - fromBuilder: builder 137 + source: /app/apps/dashboard/.next/standalone/apps/dashboard/ 138 + target: ./ 139 + chmod: "555" 140 + # Copy root node_modules (required for pnpm symlinks) 141 + - fromBuilder: builder 142 + source: /app/node_modules/ 143 + target: /app/node_modules/ 144 + # Copy static assets 145 + - fromBuilder: builder 146 + source: /app/apps/dashboard/.next/static/ 147 + target: ./.next/static/ 148 + # Copy public directory 149 + - fromBuilder: builder 150 + source: /app/apps/dashboard/public/ 151 + target: ./public/ 152 + 153 + # Install curl for health checks 154 + root: 155 + run: 156 + - apt-get update 157 + - apt-get install -y --no-install-recommends curl 158 + - rm -rf /var/lib/apt/lists/* 159 + 160 + # Security: run as non-root user 161 + user: "1000:1000" 162 + 163 + # Expose port 164 + expose: "3000" 165 + 166 + # Health check 167 + healthcheck: 168 + interval: 30s 169 + timeout: 10s 170 + start: 45s 171 + retries: 3 172 + cmd: curl -f http://localhost:3000/ || exit 1 173 + 174 + # Start application 175 + cmd: 176 + - node 177 + - server.js
+83
apps/dashboard/dofigen.yml
··· 1 + builders: 2 + # Stage 1: Next.js build with Node.js 3 + builder: 4 + fromImage: node:24-slim 5 + workdir: /app 6 + copy: 7 + - . /app/ 8 + env: 9 + NODE_ENV: production 10 + PNPM_HOME: /pnpm 11 + PATH: $PNPM_HOME:$PATH 12 + # Build-time environment variables (placeholder values, overwritten by .env.docker at runtime) 13 + DATABASE_URL: http://libsql:8080 14 + DATABASE_AUTH_TOKEN: test 15 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 16 + NEXT_PUBLIC_URL: http://localhost:3002 17 + TEAM_ID_VERCEL: test 18 + PROJECT_ID_VERCEL: test 19 + VERCEL_AUTH_BEARER_TOKEN: test 20 + OPENPANEL_CLIENT_SECRET: test 21 + RESEND_API_KEY: test 22 + UPSTASH_REDIS_REST_URL: test 23 + UPSTASH_REDIS_REST_TOKEN: test 24 + UNKEY_TOKEN: test 25 + UNKEY_API_ID: test 26 + TINY_BIRD_API_KEY: test 27 + CRON_SECRET: test 28 + STRIPE_SECRET_KEY: test 29 + AUTH_SECRET: build-time-placeholder-min-32-chars-long 30 + run: 31 + - corepack enable 32 + - pnpm install --frozen-lockfile 33 + - pnpm turbo run build --filter=@openstatus/dashboard 34 + 35 + # Runtime stage 36 + fromImage: node:24-slim 37 + workdir: /app/apps/dashboard 38 + 39 + # Copy artifacts from builder 40 + copy: 41 + # Copy Next.js standalone output 42 + - fromBuilder: builder 43 + source: /app/apps/dashboard/.next/standalone/apps/dashboard/ 44 + target: ./ 45 + chmod: "555" 46 + # Copy root node_modules (required for pnpm symlinks) 47 + - fromBuilder: builder 48 + source: /app/node_modules/ 49 + target: /app/node_modules/ 50 + # Copy static assets 51 + - fromBuilder: builder 52 + source: /app/apps/dashboard/.next/static/ 53 + target: ./.next/static/ 54 + # Copy public directory 55 + - fromBuilder: builder 56 + source: /app/apps/dashboard/public/ 57 + target: ./public/ 58 + 59 + # Install curl for health checks 60 + root: 61 + run: 62 + - apt-get update 63 + - apt-get install -y --no-install-recommends curl 64 + - rm -rf /var/lib/apt/lists/* 65 + 66 + # Security: run as non-root user 67 + user: "1000:1000" 68 + 69 + # Expose port 70 + expose: "3000" 71 + 72 + # Health check 73 + healthcheck: 74 + interval: 30s 75 + timeout: 10s 76 + start: 45s 77 + retries: 3 78 + cmd: curl -f http://localhost:3000/ || exit 1 79 + 80 + # Start application 81 + cmd: 82 + - node 83 + - server.js
+19 -28
apps/dashboard/next.config.ts
··· 1 - const { withSentryConfig } = require("@sentry/nextjs"); 2 - 1 + import { withSentryConfig } from "@sentry/nextjs"; 3 2 import type { NextConfig } from "next"; 4 3 5 4 const nextConfig: NextConfig = { 5 + ...(process.env.SELF_HOST === "true" && { output: "standalone" }), 6 6 images: { 7 7 remotePatterns: [ 8 8 new URL("https://openstatus.dev/**"), ··· 16 16 }, 17 17 }; 18 18 19 - module.exports = withSentryConfig( 20 - nextConfig, 21 - { 22 - // For all available options, see: 23 - // https://github.com/getsentry/sentry-webpack-plugin#options 19 + // For detailed options, refer to the official documentation: 20 + // - Webpack plugin options: https://github.com/getsentry/sentry-webpack-plugin#options 21 + // - Next.js Sentry setup guide: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 22 + const sentryConfig = { 23 + // Prevent log output unless running in a CI environment (helps reduce noise in logs) 24 + silent: !process.env.CI, 25 + org: "openstatus", 26 + project: "openstatus", 27 + authToken: process.env.SENTRY_AUTH_TOKEN, 24 28 25 - // Only print logs for uploading source maps in CI 26 - // Set to `true` to suppress logs 27 - silent: !process.env.CI, 29 + // Upload a larger set of source maps for improved stack trace accuracy (increases build time) 30 + widenClientFileUpload: true, 28 31 29 - org: "openstatus", 30 - project: "openstatus", 31 - }, 32 - { 33 - // For all available options, see: 34 - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 32 + // If set to true, transpiles Sentry SDK to be compatible with IE11 (increases bundle size) 33 + transpileClientSDK: false, 35 34 36 - // Pass the auth token 37 - authToken: process.env.SENTRY_AUTH_TOKEN, 35 + // Tree-shake Sentry logger statements to reduce bundle size 36 + disableLogger: true, 37 + }; 38 38 39 - // Upload a larger set of source maps for prettier stack traces (increases build time) 40 - widenClientFileUpload: true, 41 - 42 - // Transpiles SDK to be compatible with IE11 (increases bundle size) 43 - transpileClientSDK: false, 44 - 45 - // Automatically tree-shake Sentry logger statements to reduce bundle size 46 - disableLogger: true, 47 - }, 48 - ); 39 + export default withSentryConfig(nextConfig, sentryConfig);
+2 -1
apps/dashboard/src/app/(public)/login/page.tsx
··· 24 24 </p> 25 25 </div> 26 26 <div className="grid gap-3 p-4"> 27 - {process.env.NODE_ENV === "development" ? ( 27 + {process.env.NODE_ENV === "development" || 28 + process.env.SELF_HOST === "true" ? ( 28 29 <div className="grid gap-3"> 29 30 <MagicLinkForm /> 30 31 <Separator />
+1 -1
apps/dashboard/src/lib/auth/index.ts
··· 16 16 // debug: true, 17 17 adapter, 18 18 providers: 19 - process.env.NODE_ENV === "development" 19 + process.env.NODE_ENV === "development" || process.env.SELF_HOST === "true" 20 20 ? [GitHubProvider, GoogleProvider, ResendProvider] 21 21 : [GitHubProvider, GoogleProvider], 22 22 callbacks: {
+1 -1
apps/dashboard/src/lib/trpc/server.tsx
··· 31 31 headers: { 32 32 "x-trpc-source": "server", 33 33 }, 34 - async fetch(url, options) { 34 + fetch: async (url, options) => { 35 35 const cookieStore = await cookies(); 36 36 return fetch(url, { 37 37 ...options,
+3 -4
apps/dashboard/src/lib/trpc/shared.ts
··· 6 6 7 7 const getBaseUrl = () => { 8 8 if (typeof window !== "undefined") return ""; 9 - const vc = process.env.VERCEL_URL; 10 - // if (vc) return `https://${vc}`; 11 - if (vc) return "https://app.openstatus.dev"; 12 - return "http://localhost:3000"; 9 + // Note: dashboard has its own tRPC API routes 10 + if (process.env.VERCEL_URL) return "https://app.openstatus.dev"; // Vercel 11 + return "http://localhost:3000"; // Local dev and Docker (internal calls) 13 12 }; 14 13 15 14 const lambdas = ["stripeRouter", "emailRouter"];
+3
apps/private-location/.dockerignore
··· 1 + # This file is generated by Dofigen v2.5.1 2 + # See https://github.com/lenra-io/dofigen 3 +
+60 -27
apps/private-location/Dockerfile
··· 1 - FROM --platform=$BUILDPLATFORM golang:1.25-alpine as builder 1 + # syntax=docker/dockerfile:1.11 2 + # This file is generated by Dofigen v2.5.1 3 + # See https://github.com/lenra-io/dofigen 2 4 5 + # builder 6 + FROM golang@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder 7 + ARG TARGETARCH 3 8 ARG TARGETOS 4 - ARG TARGETARCH 5 - 9 + LABEL \ 10 + org.opencontainers.image.base.digest="sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb" \ 11 + org.opencontainers.image.base.name="docker.io/golang:1.25-alpine" \ 12 + org.opencontainers.image.stage="builder" 13 + ENV \ 14 + CGO_ENABLED="0" \ 15 + TZ="UTC" 6 16 WORKDIR /go/src/app 7 - 8 - RUN apk add --no-cache tzdata 9 - ENV TZ=UTC 10 - 11 - ENV CGO_ENABLED=0 17 + COPY \ 18 + --link \ 19 + "." "." 20 + RUN <<EOF 21 + apk add --no-cache tzdata 22 + go mod download 23 + GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o private-location ./cmd/server 24 + EOF 12 25 13 - COPY go.* . 14 - RUN go mod download 15 - 16 - COPY . . 17 - RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o private-location ./cmd/server 18 - 19 - FROM scratch 20 - 26 + # runtime 27 + FROM alpine@sha256:5405e8f36ce1878720f71217d664aa3dea32e5e5df11acbf07fc78ef5661465b AS runtime 28 + LABEL \ 29 + io.dofigen.version="2.5.1" \ 30 + org.opencontainers.image.authors="OpenStatus Team" \ 31 + org.opencontainers.image.base.digest="sha256:5405e8f36ce1878720f71217d664aa3dea32e5e5df11acbf07fc78ef5661465b" \ 32 + org.opencontainers.image.base.name="docker.io/alpine:3.21" \ 33 + org.opencontainers.image.description="Private location orchestrator for OpenStatus" \ 34 + org.opencontainers.image.source="https://github.com/openstatusHQ/openstatus" \ 35 + org.opencontainers.image.title="OpenStatus Private Location" \ 36 + org.opencontainers.image.vendor="OpenStatus" 37 + ENV \ 38 + GIN_MODE="release" \ 39 + TZ="UTC" \ 40 + USER="1000" 21 41 WORKDIR /opt/bin 22 - 23 - COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 24 - COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo 25 - 26 - COPY --from=builder /go/src/app/private-location /opt/bin/private-location 27 - 28 - ENV TZ=UTC 29 - ENV USER=1000 30 - ENV GIN_MODE=release 31 - 32 - CMD [ "/opt/bin/private-location" ] 42 + COPY \ 43 + --from=builder \ 44 + --chown=1000:1000 \ 45 + --link \ 46 + "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/" 47 + COPY \ 48 + --from=builder \ 49 + --chown=1000:1000 \ 50 + --link \ 51 + "/usr/share/zoneinfo" "/usr/share/zoneinfo" 52 + COPY \ 53 + --from=builder \ 54 + --chown=1000:1000 \ 55 + --link \ 56 + "/go/src/app/private-location" "/opt/bin/private-location" 57 + USER 1000:1000 58 + EXPOSE 8080 59 + HEALTHCHECK \ 60 + --interval=15s \ 61 + --timeout=10s \ 62 + --start-period=30s \ 63 + --retries=3 \ 64 + CMD wget --spider -q http://localhost:8080/health || exit 1 65 + CMD ["/opt/bin/private-location"]
+153
apps/private-location/dofigen.lock
··· 1 + effective: | 2 + builders: 3 + builder: 4 + fromImage: 5 + path: golang 6 + digest: sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb 7 + label: 8 + org.opencontainers.image.base.name: docker.io/golang:1.25-alpine 9 + org.opencontainers.image.stage: builder 10 + org.opencontainers.image.base.digest: sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb 11 + workdir: /go/src/app 12 + arg: 13 + TARGETOS: '' 14 + TARGETARCH: '' 15 + env: 16 + CGO_ENABLED: '0' 17 + TZ: UTC 18 + copy: 19 + - paths: 20 + - . 21 + target: . 22 + run: 23 + - apk add --no-cache tzdata 24 + - go mod download 25 + - GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o private-location ./cmd/server 26 + fromImage: 27 + path: alpine 28 + digest: sha256:5405e8f36ce1878720f71217d664aa3dea32e5e5df11acbf07fc78ef5661465b 29 + label: 30 + org.opencontainers.image.description: Private location orchestrator for OpenStatus 31 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 32 + org.opencontainers.image.title: OpenStatus Private Location 33 + org.opencontainers.image.vendor: OpenStatus 34 + org.opencontainers.image.base.digest: sha256:5405e8f36ce1878720f71217d664aa3dea32e5e5df11acbf07fc78ef5661465b 35 + io.dofigen.version: 2.5.1 36 + org.opencontainers.image.authors: OpenStatus Team 37 + org.opencontainers.image.base.name: docker.io/alpine:3.21 38 + user: 39 + user: '1000' 40 + group: '1000' 41 + workdir: /opt/bin 42 + env: 43 + GIN_MODE: release 44 + TZ: UTC 45 + USER: '1000' 46 + copy: 47 + - fromBuilder: builder 48 + paths: 49 + - /etc/ssl/certs/ca-certificates.crt 50 + target: /etc/ssl/certs/ 51 + - fromBuilder: builder 52 + paths: 53 + - /usr/share/zoneinfo 54 + target: /usr/share/zoneinfo 55 + - fromBuilder: builder 56 + paths: 57 + - /go/src/app/private-location 58 + target: /opt/bin/private-location 59 + cmd: 60 + - /opt/bin/private-location 61 + expose: 62 + - port: 8080 63 + healthcheck: 64 + cmd: wget --spider -q http://localhost:8080/health || exit 1 65 + interval: 15s 66 + timeout: 10s 67 + start: 30s 68 + retries: 3 69 + images: 70 + docker.io: 71 + library: 72 + golang: 73 + 1.25-alpine: 74 + digest: sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb 75 + alpine: 76 + '3.21': 77 + digest: sha256:5405e8f36ce1878720f71217d664aa3dea32e5e5df11acbf07fc78ef5661465b 78 + resources: 79 + dofigen.yml: 80 + hash: 515b6b2ef60b8f0ea954fce96aed5dc7aadf75ceefa6c9ea56f514ac14e7422b 81 + content: | 82 + builders: 83 + # Stage 1: Build Go binary 84 + builder: 85 + fromImage: golang:1.25-alpine 86 + platform: $BUILDPLATFORM 87 + label: 88 + org.opencontainers.image.stage: builder 89 + workdir: /go/src/app 90 + # Build-time arguments (overwritten by .env.docker at runtime) 91 + args: 92 + TARGETOS: "" 93 + TARGETARCH: "" 94 + env: 95 + TZ: UTC 96 + CGO_ENABLED: "0" 97 + copy: 98 + # Copy source code 99 + - . . 100 + run: 101 + - apk add --no-cache tzdata 102 + - go mod download 103 + # Build optimized binary 104 + # -trimpath: Remove file system paths from binary 105 + # -ldflags "-s -w": Strip debug info and symbol table 106 + - GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o private-location ./cmd/server 107 + 108 + # Runtime stage 109 + fromImage: alpine:3.21 110 + 111 + # Metadata labels 112 + label: 113 + org.opencontainers.image.title: OpenStatus Private Location 114 + org.opencontainers.image.description: Private location orchestrator for OpenStatus 115 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 116 + org.opencontainers.image.vendor: OpenStatus 117 + org.opencontainers.image.authors: OpenStatus Team 118 + workdir: /opt/bin 119 + 120 + # Copy artifacts from builder 121 + copy: 122 + - fromBuilder: builder 123 + source: /etc/ssl/certs/ca-certificates.crt 124 + target: /etc/ssl/certs/ 125 + - fromBuilder: builder 126 + source: /usr/share/zoneinfo 127 + target: /usr/share/zoneinfo 128 + - fromBuilder: builder 129 + source: /go/src/app/private-location 130 + target: /opt/bin/private-location 131 + 132 + env: 133 + TZ: UTC 134 + USER: "1000" 135 + GIN_MODE: release 136 + 137 + # Security: run as non-root user 138 + user: "1000:1000" 139 + 140 + # Expose port 141 + expose: "8080" 142 + 143 + # Health check 144 + healthcheck: 145 + interval: 15s 146 + timeout: 10s 147 + start: 30s 148 + retries: 3 149 + cmd: wget --spider -q http://localhost:8080/health || exit 1 150 + 151 + # Start application 152 + cmd: 153 + - /opt/bin/private-location
+72
apps/private-location/dofigen.yml
··· 1 + builders: 2 + # Stage 1: Build Go binary 3 + builder: 4 + fromImage: golang:1.25-alpine 5 + platform: $BUILDPLATFORM 6 + label: 7 + org.opencontainers.image.stage: builder 8 + workdir: /go/src/app 9 + # Build-time arguments (overwritten by .env.docker at runtime) 10 + args: 11 + TARGETOS: "" 12 + TARGETARCH: "" 13 + env: 14 + TZ: UTC 15 + CGO_ENABLED: "0" 16 + copy: 17 + # Copy source code 18 + - . . 19 + run: 20 + - apk add --no-cache tzdata 21 + - go mod download 22 + # Build optimized binary 23 + # -trimpath: Remove file system paths from binary 24 + # -ldflags "-s -w": Strip debug info and symbol table 25 + - GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o private-location ./cmd/server 26 + 27 + # Runtime stage 28 + fromImage: alpine:3.21 29 + 30 + # Metadata labels 31 + label: 32 + org.opencontainers.image.title: OpenStatus Private Location 33 + org.opencontainers.image.description: Private location orchestrator for OpenStatus 34 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 35 + org.opencontainers.image.vendor: OpenStatus 36 + org.opencontainers.image.authors: OpenStatus Team 37 + workdir: /opt/bin 38 + 39 + # Copy artifacts from builder 40 + copy: 41 + - fromBuilder: builder 42 + source: /etc/ssl/certs/ca-certificates.crt 43 + target: /etc/ssl/certs/ 44 + - fromBuilder: builder 45 + source: /usr/share/zoneinfo 46 + target: /usr/share/zoneinfo 47 + - fromBuilder: builder 48 + source: /go/src/app/private-location 49 + target: /opt/bin/private-location 50 + 51 + env: 52 + TZ: UTC 53 + USER: "1000" 54 + GIN_MODE: release 55 + 56 + # Security: run as non-root user 57 + user: "1000:1000" 58 + 59 + # Expose port 60 + expose: "8080" 61 + 62 + # Health check 63 + healthcheck: 64 + interval: 15s 65 + timeout: 10s 66 + start: 30s 67 + retries: 3 68 + cmd: wget --spider -q http://localhost:8080/health || exit 1 69 + 70 + # Start application 71 + cmd: 72 + - /opt/bin/private-location
+12 -2
apps/private-location/internal/tinybird/client.go
··· 7 7 "fmt" 8 8 "net/http" 9 9 "net/url" 10 + "os" 10 11 11 12 "github.com/rs/zerolog/log" 12 13 ) 13 14 14 - const baseURL = "https://api.tinybird.co/v0/events" 15 + func getBaseURL() string { 16 + // Use local Tinybird container if available (Docker/self-hosted) 17 + // https://www.tinybird.co/docs/api-reference 18 + if tinybirdURL := os.Getenv("TINYBIRD_URL"); tinybirdURL != "" { 19 + return tinybirdURL + "/v0/events" 20 + } 21 + return "https://api.tinybird.co/v0/events" 22 + } 15 23 16 24 type Client interface { 17 25 SendEvent(ctx context.Context, event any, dataSourceName string) error ··· 20 28 type client struct { 21 29 httpClient *http.Client 22 30 apiKey string 31 + baseURL string 23 32 } 24 33 25 34 func NewClient(httpClient *http.Client, apiKey string) Client { 26 35 return client{ 27 36 httpClient: httpClient, 28 37 apiKey: apiKey, 38 + baseURL: getBaseURL(), 29 39 } 30 40 } 31 41 32 42 func (c client) SendEvent(ctx context.Context, event any, dataSourceName string) error { 33 - requestURL, err := url.Parse(baseURL) 43 + requestURL, err := url.Parse(c.baseURL) 34 44 if err != nil { 35 45 log.Ctx(ctx).Error().Err(err).Msg("unable to parse url") 36 46 return fmt.Errorf("unable to parse url: %w", err)
+25 -7
apps/server/Dockerfile
··· 6 6 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS install 7 7 LABEL \ 8 8 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 9 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 9 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 10 + org.opencontainers.image.stage="install" 10 11 WORKDIR /app/ 11 12 RUN \ 12 13 --mount=type=bind,target=bunfig.toml,source=bunfig.toml \ ··· 23 24 --mount=type=bind,target=packages/utils/package.json,source=packages/utils/package.json \ 24 25 --mount=type=bind,target=packages/tsconfig/package.json,source=packages/tsconfig/package.json \ 25 26 --mount=type=bind,target=packages/assertions/package.json,source=packages/assertions/package.json \ 26 - --mount=type=bind,target=packages/regions/package.json,source=packages/regions/package.json \ 27 27 --mount=type=bind,target=packages/theme-store/package.json,source=packages/theme-store/package.json \ 28 28 --mount=type=cache,target=/root/.bun/install/cache,sharing=locked \ 29 - bun install --production --frozen-lockfile --verbose 29 + bun install --production --frozen-lockfile --verbose 30 30 31 31 # build 32 32 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS build 33 33 LABEL \ 34 34 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 35 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 35 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 36 + org.opencontainers.image.stage="build" 36 37 ENV NODE_ENV="production" 37 38 WORKDIR /app/apps/server 38 39 COPY \ ··· 45 46 RUN bun build --compile --sourcemap src/index.ts --outfile=app 46 47 47 48 # runtime 48 - FROM debian@sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b AS runtime 49 + FROM debian@sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b AS runtime 49 50 LABEL \ 50 51 io.dofigen.version="2.5.1" \ 51 - org.opencontainers.image.base.digest="sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b" \ 52 - org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" 52 + org.opencontainers.image.authors="OpenStatus Team" \ 53 + org.opencontainers.image.base.digest="sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b" \ 54 + org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" \ 55 + org.opencontainers.image.description="REST API server with Hono framework for OpenStatus" \ 56 + org.opencontainers.image.source="https://github.com/openstatusHQ/openstatus" \ 57 + org.opencontainers.image.title="OpenStatus Server" \ 58 + org.opencontainers.image.vendor="OpenStatus" 53 59 COPY \ 54 60 --from=build \ 55 61 --chown=1000:1000 \ 56 62 --chmod=555 \ 57 63 --link \ 58 64 "/app/apps/server/app" "/bin/" 65 + USER 0:0 66 + RUN <<EOF 67 + apt-get update 68 + apt-get install -y --no-install-recommends curl 69 + rm -rf /var/lib/apt/lists/* 70 + EOF 59 71 USER 1000:1000 60 72 EXPOSE 3000 73 + HEALTHCHECK \ 74 + --interval=30s \ 75 + --timeout=10s \ 76 + --start-period=30s \ 77 + --retries=3 \ 78 + CMD curl -f http://localhost:3000/ping || exit 1 61 79 ENTRYPOINT ["/bin/app"]
+94 -38
apps/server/dofigen.lock
··· 10 10 - /packages/api 11 11 - /packages/integrations/vercel 12 12 builders: 13 - build: 13 + install: 14 14 fromImage: 15 15 path: oven/bun 16 16 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 17 17 label: 18 - org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 19 18 org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 20 - workdir: /app/apps/server 21 - env: 22 - NODE_ENV: production 23 - copy: 24 - - paths: 25 - - . 26 - target: /app/ 27 - - fromBuilder: install 28 - paths: 29 - - /app/node_modules 30 - target: /app/node_modules 31 - run: 32 - - bun build --compile --sourcemap src/index.ts --outfile=app 33 - install: 34 - fromImage: 35 - path: oven/bun 36 - digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 37 - label: 38 19 org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 39 - org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 20 + org.opencontainers.image.stage: install 40 21 workdir: /app/ 41 22 run: 42 - - bun install --production --frozen-lockfile --verbose 23 + - bun install --production --frozen-lockfile --verbose 43 24 cache: 44 25 - target: /root/.bun/install/cache 45 26 bind: ··· 71 52 source: packages/tsconfig/package.json 72 53 - target: packages/assertions/package.json 73 54 source: packages/assertions/package.json 74 - - target: packages/regions/package.json 75 - source: packages/regions/package.json 76 55 - target: packages/theme-store/package.json 77 56 source: packages/theme-store/package.json 57 + build: 58 + fromImage: 59 + path: oven/bun 60 + digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 61 + label: 62 + org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 63 + org.opencontainers.image.stage: build 64 + org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 65 + workdir: /app/apps/server 66 + env: 67 + NODE_ENV: production 68 + copy: 69 + - paths: 70 + - . 71 + target: /app/ 72 + - fromBuilder: install 73 + paths: 74 + - /app/node_modules 75 + target: /app/node_modules 76 + run: 77 + - bun build --compile --sourcemap src/index.ts --outfile=app 78 78 fromImage: 79 79 path: debian 80 - digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 80 + digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 81 81 label: 82 - org.opencontainers.image.base.digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 82 + org.opencontainers.image.vendor: OpenStatus 83 + org.opencontainers.image.title: OpenStatus Server 84 + org.opencontainers.image.description: REST API server with Hono framework for OpenStatus 83 85 io.dofigen.version: 2.5.1 86 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 87 + org.opencontainers.image.base.digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 88 + org.opencontainers.image.authors: OpenStatus Team 84 89 org.opencontainers.image.base.name: docker.io/debian:bullseye-slim 90 + user: 91 + user: '1000' 92 + group: '1000' 85 93 copy: 86 94 - fromBuilder: build 87 95 paths: 88 96 - /app/apps/server/app 89 97 target: /bin/ 90 98 chmod: '555' 99 + root: 100 + run: 101 + - apt-get update 102 + - apt-get install -y --no-install-recommends curl 103 + - rm -rf /var/lib/apt/lists/* 91 104 entrypoint: 92 105 - /bin/app 93 106 expose: 94 107 - port: 3000 108 + healthcheck: 109 + cmd: curl -f http://localhost:3000/ping || exit 1 110 + interval: 30s 111 + timeout: 10s 112 + start: 30s 113 + retries: 3 95 114 images: 96 115 docker.io: 97 116 library: 98 117 debian: 99 118 bullseye-slim: 100 - digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 119 + digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 101 120 oven: 102 121 bun: 103 122 1.3.3: 104 123 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 105 124 resources: 106 125 dofigen.yml: 107 - hash: 0c19dd086edafd9f71997f33d49b543e4f3fc36d1eb8293d17f04765bb176d97 126 + hash: f2069730c7b0f5e8be704cbfc3ae77ac031f05b2a3b916bcbe758f7955edfa5e 108 127 content: | 128 + # Files to exclude from Docker context 109 129 ignore: 110 130 - node_modules 111 131 - /apps/docs ··· 116 136 - /apps/workflows 117 137 - /packages/api 118 138 - /packages/integrations/vercel 139 + 119 140 builders: 141 + # Stage 1: Install production dependencies 120 142 install: 121 143 fromImage: oven/bun:1.3.3 122 144 workdir: /app/ 123 - # Copy project 145 + labels: 146 + org.opencontainers.image.stage: install 124 147 bind: 125 148 - bunfig.toml 126 149 - package.json ··· 136 159 - packages/utils/package.json 137 160 - packages/tsconfig/package.json 138 161 - packages/assertions/package.json 139 - - packages/regions/package.json 140 162 - packages/theme-store/package.json 141 - 142 - # Install dependencies 143 - run: bun install --production --frozen-lockfile --verbose 163 + run: bun install --production --frozen-lockfile --verbose 144 164 cache: 145 165 - /root/.bun/install/cache 166 + 167 + # Stage 2: Build application (compile to binary) 146 168 build: 147 169 fromImage: oven/bun:1.3.3 148 170 workdir: /app/apps/server 171 + labels: 172 + org.opencontainers.image.stage: build 173 + env: 174 + NODE_ENV: production 149 175 copy: 150 176 - . /app/ 151 177 - fromBuilder: install 152 178 source: /app/node_modules 153 179 target: /app/node_modules 154 - # Should set env to production here 155 - # Compile the TypeScript application 156 - env: 157 - NODE_ENV: production 158 180 run: bun build --compile --sourcemap src/index.ts --outfile=app 181 + 182 + # Runtime stage 159 183 fromImage: debian:bullseye-slim 184 + 185 + # Metadata labels 186 + labels: 187 + org.opencontainers.image.title: OpenStatus Server 188 + org.opencontainers.image.description: REST API server with Hono framework for OpenStatus 189 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 190 + org.opencontainers.image.vendor: OpenStatus 191 + org.opencontainers.image.authors: OpenStatus Team 192 + 193 + # Copy compiled binary 160 194 copy: 161 195 - fromBuilder: build 162 196 source: /app/apps/server/app 163 197 target: /bin/ 164 198 chmod: "555" 165 - expose: 3000 199 + 200 + # Install curl for health checks 201 + root: 202 + run: 203 + - apt-get update 204 + - apt-get install -y --no-install-recommends curl 205 + - rm -rf /var/lib/apt/lists/* 206 + 207 + # Security: run as non-root user 208 + user: "1000:1000" 209 + 210 + # Expose port 211 + expose: "3000" 212 + 213 + # Health check 214 + healthcheck: 215 + interval: 30s 216 + timeout: 10s 217 + start: 30s 218 + retries: 3 219 + cmd: curl -f http://localhost:3000/ping || exit 1 220 + 221 + # Start application 166 222 entrypoint: /bin/app
+47 -10
apps/server/dofigen.yml
··· 1 + # Files to exclude from Docker context 1 2 ignore: 2 3 - node_modules 3 4 - /apps/docs ··· 8 9 - /apps/workflows 9 10 - /packages/api 10 11 - /packages/integrations/vercel 12 + 11 13 builders: 14 + # Stage 1: Install production dependencies 12 15 install: 13 16 fromImage: oven/bun:1.3.3 14 17 workdir: /app/ 15 - # Copy project 18 + labels: 19 + org.opencontainers.image.stage: install 16 20 bind: 17 21 - bunfig.toml 18 22 - package.json ··· 28 32 - packages/utils/package.json 29 33 - packages/tsconfig/package.json 30 34 - packages/assertions/package.json 31 - - packages/regions/package.json 32 35 - packages/theme-store/package.json 33 - 34 - # Install dependencies 35 - run: bun install --production --frozen-lockfile --verbose 36 + run: bun install --production --frozen-lockfile --verbose 36 37 cache: 37 38 - /root/.bun/install/cache 39 + 40 + # Stage 2: Build application (compile to binary) 38 41 build: 39 42 fromImage: oven/bun:1.3.3 40 43 workdir: /app/apps/server 44 + labels: 45 + org.opencontainers.image.stage: build 46 + env: 47 + NODE_ENV: production 41 48 copy: 42 49 - . /app/ 43 50 - fromBuilder: install 44 51 source: /app/node_modules 45 52 target: /app/node_modules 46 - # Should set env to production here 47 - # Compile the TypeScript application 48 - env: 49 - NODE_ENV: production 50 53 run: bun build --compile --sourcemap src/index.ts --outfile=app 54 + 55 + # Runtime stage 51 56 fromImage: debian:bullseye-slim 57 + 58 + # Metadata labels 59 + labels: 60 + org.opencontainers.image.title: OpenStatus Server 61 + org.opencontainers.image.description: REST API server with Hono framework for OpenStatus 62 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 63 + org.opencontainers.image.vendor: OpenStatus 64 + org.opencontainers.image.authors: OpenStatus Team 65 + 66 + # Copy compiled binary 52 67 copy: 53 68 - fromBuilder: build 54 69 source: /app/apps/server/app 55 70 target: /bin/ 56 71 chmod: "555" 57 - expose: 3000 72 + 73 + # Install curl for health checks 74 + root: 75 + run: 76 + - apt-get update 77 + - apt-get install -y --no-install-recommends curl 78 + - rm -rf /var/lib/apt/lists/* 79 + 80 + # Security: run as non-root user 81 + user: "1000:1000" 82 + 83 + # Expose port 84 + expose: "3000" 85 + 86 + # Health check 87 + healthcheck: 88 + interval: 30s 89 + timeout: 10s 90 + start: 30s 91 + retries: 3 92 + cmd: curl -f http://localhost:3000/ping || exit 1 93 + 94 + # Start application 58 95 entrypoint: /bin/app
+3
apps/status-page/.dockerignore
··· 1 + # This file is generated by Dofigen v2.5.1 2 + # See https://github.com/lenra-io/dofigen 3 +
+2
apps/status-page/.env.example
··· 31 31 CRON_SECRET=yolo 32 32 UNKEY_TOKEN=yolo 33 33 UNKEY_API_ID=yolo 34 + 35 + VERCEL_URL=https://app.openstatus.dev
+82
apps/status-page/Dockerfile
··· 1 + # syntax=docker/dockerfile:1.11 2 + # This file is generated by Dofigen v2.5.1 3 + # See https://github.com/lenra-io/dofigen 4 + 5 + # builder 6 + FROM node@sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 AS builder 7 + LABEL \ 8 + org.opencontainers.image.base.digest="sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2" \ 9 + org.opencontainers.image.base.name="docker.io/node:24-slim" 10 + ENV \ 11 + VERCEL_AUTH_BEARER_TOKEN="test" \ 12 + STRIPE_SECRET_KEY="test" \ 13 + UPSTASH_REDIS_REST_TOKEN="test" \ 14 + DATABASE_AUTH_TOKEN="test" \ 15 + RESEND_API_KEY="test" \ 16 + CRON_SECRET="test" \ 17 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID="test" \ 18 + NEXT_PUBLIC_URL="http://localhost:3003" \ 19 + OPENPANEL_CLIENT_SECRET="test" \ 20 + DATABASE_URL="http://libsql:8080" \ 21 + UNKEY_API_ID="test" \ 22 + NODE_ENV="production" \ 23 + UPSTASH_REDIS_REST_URL="test" \ 24 + UNKEY_TOKEN="test" \ 25 + TINY_BIRD_API_KEY="test" \ 26 + PATH="$PNPM_HOME:$PATH" \ 27 + PNPM_HOME="/pnpm" \ 28 + PROJECT_ID_VERCEL="test" \ 29 + TEAM_ID_VERCEL="test" 30 + WORKDIR /app 31 + COPY \ 32 + --link \ 33 + "." "/app/" 34 + RUN <<EOF 35 + corepack enable 36 + pnpm install --frozen-lockfile 37 + pnpm turbo run build --filter=@openstatus/status-page 38 + EOF 39 + 40 + # runtime 41 + FROM node@sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 AS runtime 42 + LABEL \ 43 + io.dofigen.version="2.5.1" \ 44 + org.opencontainers.image.base.digest="sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2" \ 45 + org.opencontainers.image.base.name="docker.io/node:24-slim" 46 + WORKDIR /app/apps/status-page 47 + COPY \ 48 + --from=builder \ 49 + --chown=1000:1000 \ 50 + --chmod=555 \ 51 + --link \ 52 + "/app/apps/status-page/.next/standalone/apps/status-page/" "./" 53 + COPY \ 54 + --from=builder \ 55 + --chown=1000:1000 \ 56 + --link \ 57 + "/app/node_modules/" "/app/node_modules/" 58 + COPY \ 59 + --from=builder \ 60 + --chown=1000:1000 \ 61 + --link \ 62 + "/app/apps/status-page/.next/static/" "./.next/static/" 63 + COPY \ 64 + --from=builder \ 65 + --chown=1000:1000 \ 66 + --link \ 67 + "/app/apps/status-page/public/" "./public/" 68 + USER 0:0 69 + RUN <<EOF 70 + apt-get update 71 + apt-get install -y --no-install-recommends curl 72 + rm -rf /var/lib/apt/lists/* 73 + EOF 74 + USER 1000:1000 75 + EXPOSE 3000 76 + HEALTHCHECK \ 77 + --interval=30s \ 78 + --timeout=10s \ 79 + --start-period=45s \ 80 + --retries=3 \ 81 + CMD curl -f http://localhost:3000/ || exit 1 82 + CMD ["node", "server.js"]
+175
apps/status-page/dofigen.lock
··· 1 + effective: | 2 + builders: 3 + builder: 4 + fromImage: 5 + path: node 6 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 7 + label: 8 + org.opencontainers.image.base.name: docker.io/node:24-slim 9 + org.opencontainers.image.base.digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 10 + workdir: /app 11 + env: 12 + VERCEL_AUTH_BEARER_TOKEN: test 13 + STRIPE_SECRET_KEY: test 14 + UPSTASH_REDIS_REST_TOKEN: test 15 + DATABASE_AUTH_TOKEN: test 16 + RESEND_API_KEY: test 17 + CRON_SECRET: test 18 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 19 + NEXT_PUBLIC_URL: http://localhost:3003 20 + OPENPANEL_CLIENT_SECRET: test 21 + DATABASE_URL: http://libsql:8080 22 + UNKEY_API_ID: test 23 + NODE_ENV: production 24 + UPSTASH_REDIS_REST_URL: test 25 + UNKEY_TOKEN: test 26 + TINY_BIRD_API_KEY: test 27 + PATH: $PNPM_HOME:$PATH 28 + PNPM_HOME: /pnpm 29 + PROJECT_ID_VERCEL: test 30 + TEAM_ID_VERCEL: test 31 + copy: 32 + - paths: 33 + - . 34 + target: /app/ 35 + run: 36 + - corepack enable 37 + - pnpm install --frozen-lockfile 38 + - pnpm turbo run build --filter=@openstatus/status-page 39 + fromImage: 40 + path: node 41 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 42 + label: 43 + io.dofigen.version: 2.5.1 44 + org.opencontainers.image.base.name: docker.io/node:24-slim 45 + org.opencontainers.image.base.digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 46 + user: 47 + user: '1000' 48 + group: '1000' 49 + workdir: /app/apps/status-page 50 + copy: 51 + - fromBuilder: builder 52 + paths: 53 + - /app/apps/status-page/.next/standalone/apps/status-page/ 54 + target: ./ 55 + chmod: '555' 56 + - fromBuilder: builder 57 + paths: 58 + - /app/node_modules/ 59 + target: /app/node_modules/ 60 + - fromBuilder: builder 61 + paths: 62 + - /app/apps/status-page/.next/static/ 63 + target: ./.next/static/ 64 + - fromBuilder: builder 65 + paths: 66 + - /app/apps/status-page/public/ 67 + target: ./public/ 68 + root: 69 + run: 70 + - apt-get update 71 + - apt-get install -y --no-install-recommends curl 72 + - rm -rf /var/lib/apt/lists/* 73 + cmd: 74 + - node 75 + - server.js 76 + expose: 77 + - port: 3000 78 + healthcheck: 79 + cmd: curl -f http://localhost:3000/ || exit 1 80 + interval: 30s 81 + timeout: 10s 82 + start: 45s 83 + retries: 3 84 + images: 85 + docker.io: 86 + library: 87 + node: 88 + 24-slim: 89 + digest: sha256:0afb7822fac7bf9d7c1bf3b6e6c496dee6b2b64d8dfa365501a3c68e8eba94b2 90 + resources: 91 + dofigen.yml: 92 + hash: 29d846efda50082c251fd06ac8b56ba9fb73332770a8317a7536fdbcd6718c76 93 + content: | 94 + builders: 95 + # Stage 1: Next.js build with Node.js 96 + builder: 97 + fromImage: node:24-slim 98 + workdir: /app 99 + copy: 100 + - . /app/ 101 + env: 102 + NODE_ENV: production 103 + PNPM_HOME: /pnpm 104 + PATH: $PNPM_HOME:$PATH 105 + # Build-time environment variables (placeholder values, overwritten by .env.docker at runtime) 106 + DATABASE_URL: http://libsql:8080 107 + DATABASE_AUTH_TOKEN: test 108 + RESEND_API_KEY: test 109 + UPSTASH_REDIS_REST_URL: test 110 + UPSTASH_REDIS_REST_TOKEN: test 111 + TINY_BIRD_API_KEY: test 112 + STRIPE_SECRET_KEY: test 113 + PROJECT_ID_VERCEL: test 114 + TEAM_ID_VERCEL: test 115 + VERCEL_AUTH_BEARER_TOKEN: test 116 + CRON_SECRET: test 117 + UNKEY_TOKEN: test 118 + UNKEY_API_ID: test 119 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 120 + OPENPANEL_CLIENT_SECRET: test 121 + NEXT_PUBLIC_URL: http://localhost:3003 122 + run: 123 + - corepack enable 124 + - pnpm install --frozen-lockfile 125 + - pnpm turbo run build --filter=@openstatus/status-page 126 + 127 + # Runtime stage 128 + fromImage: node:24-slim 129 + workdir: /app/apps/status-page 130 + 131 + # Copy artifacts from builder 132 + copy: 133 + # Copy Next.js standalone output 134 + - fromBuilder: builder 135 + source: /app/apps/status-page/.next/standalone/apps/status-page/ 136 + target: ./ 137 + chmod: "555" 138 + # Copy root node_modules (required for pnpm symlinks) 139 + - fromBuilder: builder 140 + source: /app/node_modules/ 141 + target: /app/node_modules/ 142 + # Copy static assets 143 + - fromBuilder: builder 144 + source: /app/apps/status-page/.next/static/ 145 + target: ./.next/static/ 146 + # Copy public directory 147 + - fromBuilder: builder 148 + source: /app/apps/status-page/public/ 149 + target: ./public/ 150 + 151 + # Install curl for health checks 152 + root: 153 + run: 154 + - apt-get update 155 + - apt-get install -y --no-install-recommends curl 156 + - rm -rf /var/lib/apt/lists/* 157 + 158 + # Security: run as non-root user 159 + user: "1000:1000" 160 + 161 + # Expose port 162 + expose: "3000" 163 + 164 + # Health check 165 + healthcheck: 166 + interval: 30s 167 + timeout: 10s 168 + start: 45s 169 + retries: 3 170 + cmd: curl -f http://localhost:3000/ || exit 1 171 + 172 + # Start application 173 + cmd: 174 + - node 175 + - server.js
+82
apps/status-page/dofigen.yml
··· 1 + builders: 2 + # Stage 1: Next.js build with Node.js 3 + builder: 4 + fromImage: node:24-slim 5 + workdir: /app 6 + copy: 7 + - . /app/ 8 + env: 9 + NODE_ENV: production 10 + PNPM_HOME: /pnpm 11 + PATH: $PNPM_HOME:$PATH 12 + # Build-time environment variables (placeholder values, overwritten by .env.docker at runtime) 13 + DATABASE_URL: http://libsql:8080 14 + DATABASE_AUTH_TOKEN: test 15 + NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test 16 + NEXT_PUBLIC_URL: http://localhost:3002 17 + TEAM_ID_VERCEL: test 18 + PROJECT_ID_VERCEL: test 19 + VERCEL_AUTH_BEARER_TOKEN: test 20 + OPENPANEL_CLIENT_SECRET: test 21 + RESEND_API_KEY: test 22 + UPSTASH_REDIS_REST_URL: test 23 + UPSTASH_REDIS_REST_TOKEN: test 24 + UNKEY_TOKEN: test 25 + UNKEY_API_ID: test 26 + TINY_BIRD_API_KEY: test 27 + CRON_SECRET: test 28 + STRIPE_SECRET_KEY: test 29 + run: 30 + - corepack enable 31 + - pnpm install --frozen-lockfile 32 + - pnpm turbo run build --filter=@openstatus/status-page 33 + 34 + # Runtime stage 35 + fromImage: node:24-slim 36 + workdir: /app/apps/status-page 37 + 38 + # Copy artifacts from builder 39 + copy: 40 + # Copy Next.js standalone output 41 + - fromBuilder: builder 42 + source: /app/apps/status-page/.next/standalone/apps/status-page/ 43 + target: ./ 44 + chmod: "555" 45 + # Copy root node_modules (required for pnpm symlinks) 46 + - fromBuilder: builder 47 + source: /app/node_modules/ 48 + target: /app/node_modules/ 49 + # Copy static assets 50 + - fromBuilder: builder 51 + source: /app/apps/status-page/.next/static/ 52 + target: ./.next/static/ 53 + # Copy public directory 54 + - fromBuilder: builder 55 + source: /app/apps/status-page/public/ 56 + target: ./public/ 57 + 58 + # Install curl for health checks 59 + root: 60 + run: 61 + - apt-get update 62 + - apt-get install -y --no-install-recommends curl 63 + - rm -rf /var/lib/apt/lists/* 64 + 65 + # Security: run as non-root user 66 + user: "1000:1000" 67 + 68 + # Expose port 69 + expose: "3000" 70 + 71 + # Health check 72 + healthcheck: 73 + interval: 30s 74 + timeout: 10s 75 + start: 45s 76 + retries: 3 77 + cmd: curl -f http://localhost:3000/ || exit 1 78 + 79 + # Start application 80 + cmd: 81 + - node 82 + - server.js
+1 -1
apps/status-page/next-env.d.ts
··· 1 1 /// <reference types="next" /> 2 2 /// <reference types="next/image-types/global" /> 3 - import "./.next/types/routes.d.ts"; 3 + import "./.next/dev/types/routes.d.ts"; 4 4 5 5 // NOTE: This file should not be edited 6 6 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+19 -27
apps/status-page/next.config.ts
··· 1 - const { withSentryConfig } = require("@sentry/nextjs"); 1 + import { withSentryConfig } from "@sentry/nextjs"; 2 2 3 3 import type { NextConfig } from "next"; 4 4 5 5 const nextConfig: NextConfig = { 6 + ...(process.env.SELF_HOST === "true" && { output: "standalone" }), 6 7 experimental: { 7 8 authInterrupts: true, 8 9 }, ··· 54 55 }, 55 56 }; 56 57 57 - module.exports = withSentryConfig( 58 - nextConfig, 59 - { 60 - // For all available options, see: 61 - // https://github.com/getsentry/sentry-webpack-plugin#options 58 + // For detailed options, refer to the official documentation: 59 + // - Webpack plugin options: https://github.com/getsentry/sentry-webpack-plugin#options 60 + // - Next.js Sentry setup guide: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 61 + const sentryConfig = { 62 + // Prevent log output unless running in a CI environment (helps reduce noise in logs) 63 + silent: !process.env.CI, 64 + org: "openstatus", 65 + project: "openstatus", 66 + authToken: process.env.SENTRY_AUTH_TOKEN, 62 67 63 - // Only print logs for uploading source maps in CI 64 - // Set to `true` to suppress logs 65 - silent: !process.env.CI, 68 + // Upload a larger set of source maps for improved stack trace accuracy (increases build time) 69 + widenClientFileUpload: true, 66 70 67 - org: "openstatus", 68 - project: "openstatus", 69 - }, 70 - { 71 - // For all available options, see: 72 - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 71 + // If set to true, transpiles Sentry SDK to be compatible with IE11 (increases bundle size) 72 + transpileClientSDK: false, 73 73 74 - // Pass the auth token 75 - authToken: process.env.SENTRY_AUTH_TOKEN, 74 + // Tree-shake Sentry logger statements to reduce bundle size 75 + disableLogger: true, 76 + }; 76 77 77 - // Upload a larger set of source maps for prettier stack traces (increases build time) 78 - widenClientFileUpload: true, 79 - 80 - // Transpiles SDK to be compatible with IE11 (increases bundle size) 81 - transpileClientSDK: false, 82 - 83 - // Automatically tree-shake Sentry logger statements to reduce bundle size 84 - disableLogger: true, 85 - }, 86 - ); 78 + export default withSentryConfig(nextConfig, sentryConfig);
+7 -2
apps/status-page/src/app/(status-page)/[domain]/(public)/page.tsx
··· 28 28 import { useTRPC } from "@/lib/trpc/client"; 29 29 import { cn } from "@/lib/utils"; 30 30 import { useQuery } from "@tanstack/react-query"; 31 - import { useParams } from "next/navigation"; 31 + import { notFound, useParams } from "next/navigation"; 32 32 import { useMemo } from "react"; 33 33 34 34 export default function Page() { ··· 38 38 39 39 // NOTE: we cannot use `cardType` and `barType` here because of queryKey changes 40 40 // It wouldn't match the server prefetch keys and we would have to refetch the page here 41 - const { data: pageInitial } = useQuery( 41 + const { data: pageInitial, error } = useQuery( 42 42 trpc.statusPage.get.queryOptions({ 43 43 slug: domain, 44 44 }), 45 45 ); 46 + 47 + // Handle case where page doesn't exist or query fails 48 + if (error || (!pageInitial && domain)) { 49 + notFound(); 50 + } 46 51 47 52 const hasCustomConfig = pageInitial?.configuration 48 53 ? pageInitial.configuration.type !== barType ||
+3 -3
apps/status-page/src/components/nav/footer.tsx
··· 14 14 const { domain } = useParams<{ domain: string }>(); 15 15 const [isMounted, setIsMounted] = useState(false); 16 16 const trpc = useTRPC(); 17 - const { data: page, dataUpdatedAt } = useQuery( 18 - trpc.statusPage.get.queryOptions({ slug: domain }), 19 - ); 17 + const { data: page, dataUpdatedAt } = useQuery({ 18 + ...trpc.statusPage.get.queryOptions({ slug: domain }), 19 + }); 20 20 const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 21 21 22 22 useEffect(() => {
+3 -3
apps/status-page/src/components/nav/header.tsx
··· 53 53 export function Header(props: React.ComponentProps<"header">) { 54 54 const trpc = useTRPC(); 55 55 const { domain } = useParams<{ domain: string }>(); 56 - const { data: page } = useQuery( 57 - trpc.statusPage.get.queryOptions({ slug: domain }), 58 - ); 56 + const { data: page } = useQuery({ 57 + ...trpc.statusPage.get.queryOptions({ slug: domain }), 58 + }); 59 59 60 60 const sendPageSubscriptionMutation = useMutation( 61 61 trpc.emailRouter.sendPageSubscription.mutationOptions({}),
+4 -4
apps/status-page/src/hooks/use-pathname-prefix.ts
··· 8 8 export function usePathnamePrefix() { 9 9 const trpc = useTRPC(); 10 10 const { domain } = useParams<{ domain: string }>(); 11 - const { data: page } = useQuery( 12 - trpc.statusPage.get.queryOptions({ slug: domain }), 13 - ); 11 + const { data: page } = useQuery({ 12 + ...trpc.statusPage.get.queryOptions({ slug: domain }), 13 + }); 14 14 const [prefix, setPrefix] = useState(""); 15 15 16 16 useEffect(() => { ··· 27 27 ) { 28 28 setPrefix(""); 29 29 } else { 30 - setPrefix(pathnames[1]); 30 + setPrefix(pathnames[1] || ""); 31 31 } 32 32 } 33 33 }, [page?.customDomain]);
+1 -1
apps/status-page/src/lib/trpc/server.tsx
··· 31 31 headers: { 32 32 "x-trpc-source": "server", 33 33 }, 34 - async fetch(url, options) { 34 + fetch: async (url, options) => { 35 35 const cookieStore = await cookies(); 36 36 return fetch(url, { 37 37 ...options,
+3 -4
apps/status-page/src/lib/trpc/shared.ts
··· 6 6 7 7 const getBaseUrl = () => { 8 8 if (typeof window !== "undefined") return ""; 9 - const vc = process.env.VERCEL_URL; 10 - if (vc) return `https://${vc}`; 11 - // if (vc) return "https://app.openstatus.dev"; 12 - return "http://localhost:3000"; 9 + // Note: status-page has its own tRPC API routes 10 + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // Vercel 11 + return "http://localhost:3000"; // Local dev and Docker (internal calls) 13 12 }; 14 13 15 14 const lambdas = ["stripeRouter", "emailRouter"];
+8
apps/status-page/src/proxy.ts
··· 184 184 // biome-ignore lint: to fix later 185 185 host = window.location.host; 186 186 } 187 + 188 + // Exclude localhost and IP addresses from being treated as subdomains 189 + if ( 190 + host?.match(/^(localhost|127\\.0\\.0\\.1|::1|\\d+\\.\\d+\\.\\d+\\.\\d+)/) 191 + ) { 192 + return null; 193 + } 194 + 187 195 // we should improve here for custom vercel deploy page 188 196 if (host?.includes(".") && !host.includes(".vercel.app")) { 189 197 const candidate = host.split(".")[0];
+2 -1
apps/web/.env.example
··· 50 50 GCP_PRIVATE_KEY= 51 51 CRON_SECRET= 52 52 53 - EXTERNAL_API_URL= 53 + EXTERNAL_API_URL=http://server:3000 54 + VERCEL_URL=https://app.openstatus.dev 54 55 55 56 PLAYGROUND_UNKEY_API_KEY= 56 57
+18 -27
apps/web/next.config.ts
··· 1 - const { withSentryConfig } = require("@sentry/nextjs"); 1 + import { withSentryConfig } from "@sentry/nextjs"; 2 2 import type { NextConfig } from "next"; 3 3 4 4 // REMINDER: avoid Clickjacking attacks by setting the X-Frame-Options header ··· 221 221 }, 222 222 }; 223 223 224 - module.exports = withSentryConfig( 225 - nextConfig, 226 - { 227 - // For all available options, see: 228 - // https://github.com/getsentry/sentry-webpack-plugin#options 224 + // For detailed options, refer to the official documentation: 225 + // - Webpack plugin options: https://github.com/getsentry/sentry-webpack-plugin#options 226 + // - Next.js Sentry setup guide: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 227 + const sentryConfig = { 228 + // Prevent log output unless running in a CI environment (helps reduce noise in logs) 229 + silent: !process.env.CI, 230 + org: "openstatus", 231 + project: "openstatus", 232 + authToken: process.env.SENTRY_AUTH_TOKEN, 229 233 230 - // Only print logs for uploading source maps in CI 231 - // Set to `true` to suppress logs 232 - silent: !process.env.CI, 234 + // Upload a larger set of source maps for improved stack trace accuracy (increases build time) 235 + widenClientFileUpload: true, 233 236 234 - org: "openstatus", 235 - project: "openstatus", 236 - }, 237 - { 238 - // For all available options, see: 239 - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ 237 + // If set to true, transpiles Sentry SDK to be compatible with IE11 (increases bundle size) 238 + transpileClientSDK: false, 240 239 241 - // Pass the auth token 242 - authToken: process.env.SENTRY_AUTH_TOKEN, 240 + // Tree-shake Sentry logger statements to reduce bundle size 241 + disableLogger: true, 242 + }; 243 243 244 - // Upload a larger set of source maps for prettier stack traces (increases build time) 245 - widenClientFileUpload: true, 246 - 247 - // Transpiles SDK to be compatible with IE11 (increases bundle size) 248 - transpileClientSDK: false, 249 - 250 - // Automatically tree-shake Sentry logger statements to reduce bundle size 251 - disableLogger: true, 252 - }, 253 - ); 244 + export default withSentryConfig(nextConfig, sentryConfig);
+2 -3
apps/web/src/trpc/shared.ts
··· 6 6 7 7 const getBaseUrl = () => { 8 8 if (typeof window !== "undefined") return ""; 9 - const vc = process.env.VERCEL_URL; 10 - if (vc) return `https://${vc}`; 11 - return "http://localhost:3000"; 9 + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // Vercel 10 + return "http://localhost:3000"; // Local dev 12 11 }; 13 12 14 13 const lambdas = ["stripeRouter", "emailRouter"];
+39 -12
apps/workflows/Dockerfile
··· 3 3 # See https://github.com/lenra-io/dofigen 4 4 5 5 # ca-certs 6 - FROM debian@sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b AS ca-certs 6 + FROM debian@sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b AS ca-certs 7 7 LABEL \ 8 - org.opencontainers.image.base.digest="sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b" \ 9 - org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" 10 - RUN apt update && apt install -y ca-certificates && update-ca-certificates 8 + org.opencontainers.image.base.digest="sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b" \ 9 + org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" \ 10 + org.opencontainers.image.stage="ca-certs" 11 + RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && update-ca-certificates && rm -rf /var/lib/apt/lists/* 11 12 12 13 # docker 13 14 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS docker 14 15 LABEL \ 15 16 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 16 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 17 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 18 + org.opencontainers.image.stage="docker" 17 19 WORKDIR /app/apps/workflows 18 20 COPY \ 19 21 --link \ ··· 24 26 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS install 25 27 LABEL \ 26 28 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 27 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 29 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 30 + org.opencontainers.image.stage="install" 28 31 WORKDIR /app/ 29 32 RUN \ 30 33 --mount=type=bind,target=bunfig.toml,source=bunfig.toml \ ··· 48 51 --mount=type=bind,target=packages/upstash/package.json,source=packages/upstash/package.json \ 49 52 --mount=type=bind,target=packages/theme-store/package.json,source=packages/theme-store/package.json \ 50 53 --mount=type=cache,target=/root/.bun/install/cache,sharing=locked \ 51 - bun install --production --frozen-lockfile --verbose 54 + bun install --production --frozen-lockfile --verbose 52 55 53 56 # build 54 57 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS build 55 58 LABEL \ 56 59 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 57 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 60 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 61 + org.opencontainers.image.stage="build" 58 62 ENV NODE_ENV="production" 59 63 WORKDIR /app/apps/workflows 60 64 COPY \ ··· 70 74 FROM oven/bun@sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 AS libsql 71 75 LABEL \ 72 76 org.opencontainers.image.base.digest="sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4" \ 73 - org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" 77 + org.opencontainers.image.base.name="docker.io/oven/bun:1.3.3" \ 78 + org.opencontainers.image.stage="libsql" 74 79 WORKDIR /app/ 75 80 COPY \ 76 81 --from=docker \ ··· 79 84 RUN bun install 80 85 81 86 # runtime 82 - FROM debian@sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b AS runtime 87 + FROM debian@sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b AS runtime 83 88 LABEL \ 84 89 io.dofigen.version="2.5.1" \ 85 - org.opencontainers.image.base.digest="sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b" \ 86 - org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" 90 + org.opencontainers.image.authors="OpenStatus Team" \ 91 + org.opencontainers.image.base.digest="sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b" \ 92 + org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" \ 93 + org.opencontainers.image.description="Background job processing and probe scheduling for OpenStatus" \ 94 + org.opencontainers.image.source="https://github.com/openstatusHQ/openstatus" \ 95 + org.opencontainers.image.title="OpenStatus Workflows" \ 96 + org.opencontainers.image.vendor="OpenStatus" 87 97 WORKDIR /app/ 88 98 COPY \ 89 99 --from=build \ ··· 91 101 --chmod=555 \ 92 102 --link \ 93 103 "/app/apps/workflows/app" "/app/apps/workflows/" 104 + COPY \ 105 + --from=build \ 106 + --chown=1000:1000 \ 107 + --link \ 108 + "/app/data" "/app/data" 94 109 COPY \ 95 110 --from=libsql \ 96 111 --chown=1000:1000 \ ··· 106 121 --chown=1000:1000 \ 107 122 --link \ 108 123 "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/" 124 + USER 0:0 125 + RUN <<EOF 126 + apt-get update 127 + apt-get install -y --no-install-recommends curl 128 + rm -rf /var/lib/apt/lists/* 129 + EOF 109 130 USER 1000:1000 110 131 EXPOSE 3000 132 + HEALTHCHECK \ 133 + --interval=30s \ 134 + --timeout=10s \ 135 + --start-period=30s \ 136 + --retries=3 \ 137 + CMD curl -f http://localhost:3000/ping || exit 1 111 138 ENTRYPOINT ["/app/apps/workflows/app"]
+139 -59
apps/workflows/dofigen.lock
··· 12 12 - /packages/error 13 13 - /packages/tracker 14 14 builders: 15 - build: 15 + docker: 16 16 fromImage: 17 17 path: oven/bun 18 18 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 19 19 label: 20 20 org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 21 + org.opencontainers.image.stage: docker 21 22 org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 22 23 workdir: /app/apps/workflows 23 - env: 24 - NODE_ENV: production 25 24 copy: 26 25 - paths: 27 26 - . 28 27 target: /app/ 29 - - fromBuilder: install 28 + run: 29 + - bun run src/build-docker.ts 30 + libsql: 31 + fromImage: 32 + path: oven/bun 33 + digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 34 + label: 35 + org.opencontainers.image.stage: libsql 36 + org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 37 + org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 38 + workdir: /app/ 39 + copy: 40 + - fromBuilder: docker 30 41 paths: 31 - - /app/node_modules 32 - target: /app/node_modules 42 + - /app/apps/build-docker/package.json 43 + target: /app/package.json 33 44 run: 34 - - bun build --compile --target bun --sourcemap --format=cjs src/index.ts --outfile=app --external '@libsql/*' --external libsql 45 + - bun install 35 46 install: 36 47 fromImage: 37 48 path: oven/bun 38 49 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 39 50 label: 51 + org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 52 + org.opencontainers.image.stage: install 40 53 org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 41 - org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 42 54 workdir: /app/ 43 55 run: 44 - - bun install --production --frozen-lockfile --verbose 56 + - bun install --production --frozen-lockfile --verbose 45 57 cache: 46 58 - target: /root/.bun/install/cache 47 59 bind: ··· 85 97 source: packages/upstash/package.json 86 98 - target: packages/theme-store/package.json 87 99 source: packages/theme-store/package.json 88 - docker: 100 + ca-certs: 101 + fromImage: 102 + path: debian 103 + digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 104 + label: 105 + org.opencontainers.image.base.digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 106 + org.opencontainers.image.base.name: docker.io/debian:bullseye-slim 107 + org.opencontainers.image.stage: ca-certs 108 + run: 109 + - apt-get update && apt-get install -y --no-install-recommends ca-certificates && update-ca-certificates && rm -rf /var/lib/apt/lists/* 110 + build: 89 111 fromImage: 90 112 path: oven/bun 91 113 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 92 114 label: 115 + org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 116 + org.opencontainers.image.stage: build 93 117 org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 94 - org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 95 118 workdir: /app/apps/workflows 119 + env: 120 + NODE_ENV: production 96 121 copy: 97 122 - paths: 98 123 - . 99 124 target: /app/ 100 - run: 101 - - bun run src/build-docker.ts 102 - libsql: 103 - fromImage: 104 - path: oven/bun 105 - digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 106 - label: 107 - org.opencontainers.image.base.digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 108 - org.opencontainers.image.base.name: docker.io/oven/bun:1.3.3 109 - workdir: /app/ 110 - copy: 111 - - fromBuilder: docker 125 + - fromBuilder: install 112 126 paths: 113 - - /app/apps/build-docker/package.json 114 - target: /app/package.json 127 + - /app/node_modules 128 + target: /app/node_modules 115 129 run: 116 - - bun install 117 - ca-certs: 118 - fromImage: 119 - path: debian 120 - digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 121 - label: 122 - org.opencontainers.image.base.name: docker.io/debian:bullseye-slim 123 - org.opencontainers.image.base.digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 124 - run: 125 - - apt update && apt install -y ca-certificates && update-ca-certificates 130 + - bun build --compile --target bun --sourcemap --format=cjs src/index.ts --outfile=app --external '@libsql/*' --external libsql 126 131 fromImage: 127 132 path: debian 128 - digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 133 + digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 129 134 label: 130 - org.opencontainers.image.base.digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 131 135 io.dofigen.version: 2.5.1 136 + org.opencontainers.image.vendor: OpenStatus 132 137 org.opencontainers.image.base.name: docker.io/debian:bullseye-slim 138 + org.opencontainers.image.authors: OpenStatus Team 139 + org.opencontainers.image.title: OpenStatus Workflows 140 + org.opencontainers.image.base.digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 141 + org.opencontainers.image.description: Background job processing and probe scheduling for OpenStatus 142 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 143 + user: 144 + user: '1000' 145 + group: '1000' 133 146 workdir: /app/ 134 147 copy: 135 148 - fromBuilder: build ··· 137 150 - /app/apps/workflows/app 138 151 target: /app/apps/workflows/ 139 152 chmod: '555' 153 + - fromBuilder: build 154 + paths: 155 + - /app/data 156 + target: /app/data 140 157 - fromBuilder: libsql 141 158 paths: 142 159 - /app/node_modules ··· 149 166 paths: 150 167 - /etc/ssl/certs/ca-certificates.crt 151 168 target: /etc/ssl/certs/ 169 + root: 170 + run: 171 + - apt-get update 172 + - apt-get install -y --no-install-recommends curl 173 + - rm -rf /var/lib/apt/lists/* 152 174 entrypoint: 153 175 - /app/apps/workflows/app 154 176 expose: 155 177 - port: 3000 178 + healthcheck: 179 + cmd: curl -f http://localhost:3000/ping || exit 1 180 + interval: 30s 181 + timeout: 10s 182 + start: 30s 183 + retries: 3 156 184 images: 157 185 docker.io: 158 - library: 159 - debian: 160 - bullseye-slim: 161 - digest: sha256:52927eff8153b563244f98cdc802ba97918afcdf67f9e4867cbf1f7afb3d147b 162 186 oven: 163 187 bun: 164 188 1.3.3: 165 189 digest: sha256:fbf8e67e9d3b806c86be7a2f2e9bae801f2d9212a21db4dcf8cc9889f5a3c9c4 190 + library: 191 + debian: 192 + bullseye-slim: 193 + digest: sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b 166 194 resources: 167 195 dofigen.yml: 168 - hash: ec493379c3fc2d9f702bc47f7721df55c48aed071ef1c103c7f10579112bb1ad 196 + hash: 472e3606f77fb44e6ea0e59ab4cc632df9efaa6938fe3979197eebc12697b023 169 197 content: | 198 + # Files to exclude from Docker context 170 199 ignore: 171 200 - node_modules 172 201 - /apps/docs ··· 179 208 - /packages/api 180 209 - /packages/error 181 210 - /packages/tracker 211 + 182 212 builders: 213 + # Stage 1: CA Certificates 214 + ca-certs: 215 + fromImage: debian:bullseye-slim 216 + labels: 217 + org.opencontainers.image.stage: ca-certs 218 + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates && update-ca-certificates && rm -rf /var/lib/apt/lists/* 219 + 220 + # Stage 2: Docker-specific build artifacts (no dependencies) 221 + docker: 222 + fromImage: oven/bun:1.3.3 223 + workdir: /app/apps/workflows 224 + labels: 225 + org.opencontainers.image.stage: docker 226 + copy: 227 + - . /app/ 228 + run: bun run src/build-docker.ts 229 + 230 + # Stage 3: Install production dependencies 183 231 install: 184 232 fromImage: oven/bun:1.3.3 185 233 workdir: /app/ 186 - # Copy project 234 + labels: 235 + org.opencontainers.image.stage: install 187 236 bind: 188 237 - bunfig.toml 189 238 - package.json ··· 205 254 - packages/tinybird/package.json 206 255 - packages/upstash/package.json 207 256 - packages/theme-store/package.json 208 - # Install dependencies 209 - run: bun install --production --frozen-lockfile --verbose 257 + run: bun install --production --frozen-lockfile --verbose 210 258 cache: 211 259 - /root/.bun/install/cache 260 + 261 + # Stage 4: Build application (compile to binary) 212 262 build: 213 263 fromImage: oven/bun:1.3.3 214 264 workdir: /app/apps/workflows 265 + labels: 266 + org.opencontainers.image.stage: build 267 + env: 268 + NODE_ENV: production 215 269 copy: 216 270 - . /app/ 217 271 - fromBuilder: install ··· 219 273 target: /app/node_modules 220 274 # Should set env to production here 221 275 # Compile the TypeScript application 222 - env: 223 - NODE_ENV: production 224 276 run: bun build --compile --target bun --sourcemap --format=cjs src/index.ts --outfile=app --external '@libsql/*' --external libsql 225 277 226 - docker: 227 - fromImage: oven/bun:1.3.3 228 - workdir: /app/apps/workflows 229 - copy: 230 - - . /app/ 231 - run: bun run src/build-docker.ts 232 - 278 + # Stage 5: LibSQL dependencies 233 279 libsql: 234 280 fromImage: oven/bun:1.3.3 235 281 workdir: /app/ 282 + labels: 283 + org.opencontainers.image.stage: libsql 236 284 copy: 237 285 - fromBuilder: docker 238 286 source: /app/apps/build-docker/package.json 239 287 target: /app/package.json 240 288 run: bun install 241 289 242 - ca-certs: 243 - fromImage: debian:bullseye-slim 244 - run: apt update && apt install -y ca-certificates && update-ca-certificates 245 - 290 + # Runtime 246 291 fromImage: debian:bullseye-slim 247 292 workdir: /app/ 293 + 294 + # Metadata labels 295 + labels: 296 + org.opencontainers.image.title: OpenStatus Workflows 297 + org.opencontainers.image.description: Background job processing and probe scheduling for OpenStatus 298 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 299 + org.opencontainers.image.vendor: OpenStatus 300 + org.opencontainers.image.authors: OpenStatus Team 301 + 302 + # Copy artifacts from build stages 248 303 copy: 249 304 - fromBuilder: build 250 305 source: /app/apps/workflows/app 251 306 target: /app/apps/workflows/ 252 307 chmod: "555" 308 + - fromBuilder: build 309 + source: /app/data 310 + target: /app/data 253 311 - fromBuilder: libsql 254 312 source: /app/node_modules 255 313 target: /app/packages/db/node_modules ··· 259 317 - fromBuilder: ca-certs 260 318 source: /etc/ssl/certs/ca-certificates.crt 261 319 target: /etc/ssl/certs/ 262 - expose: 3000 320 + 321 + # Install curl for health checks 322 + root: 323 + run: 324 + - apt-get update 325 + - apt-get install -y --no-install-recommends curl 326 + - rm -rf /var/lib/apt/lists/* 327 + 328 + # Security: run as non-root user 329 + user: "1000:1000" 330 + 331 + # Expose port 332 + expose: "3000" 333 + 334 + # Health check 335 + healthcheck: 336 + interval: 30s 337 + timeout: 10s 338 + start: 30s 339 + retries: 3 340 + cmd: curl -f http://localhost:3000/ping || exit 1 341 + 342 + # Start application 263 343 entrypoint: /app/apps/workflows/app
+69 -17
apps/workflows/dofigen.yml
··· 1 + # Files to exclude from Docker context 1 2 ignore: 2 3 - node_modules 3 4 - /apps/docs ··· 10 11 - /packages/api 11 12 - /packages/error 12 13 - /packages/tracker 14 + 13 15 builders: 16 + # Stage 1: CA Certificates 17 + ca-certs: 18 + fromImage: debian:bullseye-slim 19 + labels: 20 + org.opencontainers.image.stage: ca-certs 21 + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates && update-ca-certificates && rm -rf /var/lib/apt/lists/* 22 + 23 + # Stage 2: Docker-specific build artifacts (no dependencies) 24 + docker: 25 + fromImage: oven/bun:1.3.3 26 + workdir: /app/apps/workflows 27 + labels: 28 + org.opencontainers.image.stage: docker 29 + copy: 30 + - . /app/ 31 + run: bun run src/build-docker.ts 32 + 33 + # Stage 3: Install production dependencies 14 34 install: 15 35 fromImage: oven/bun:1.3.3 16 36 workdir: /app/ 17 - # Copy project 37 + labels: 38 + org.opencontainers.image.stage: install 18 39 bind: 19 40 - bunfig.toml 20 41 - package.json ··· 36 57 - packages/tinybird/package.json 37 58 - packages/upstash/package.json 38 59 - packages/theme-store/package.json 39 - # Install dependencies 40 - run: bun install --production --frozen-lockfile --verbose 60 + run: bun install --production --frozen-lockfile --verbose 41 61 cache: 42 62 - /root/.bun/install/cache 63 + 64 + # Stage 4: Build application (compile to binary) 43 65 build: 44 66 fromImage: oven/bun:1.3.3 45 67 workdir: /app/apps/workflows 68 + labels: 69 + org.opencontainers.image.stage: build 70 + env: 71 + NODE_ENV: production 46 72 copy: 47 73 - . /app/ 48 74 - fromBuilder: install ··· 50 76 target: /app/node_modules 51 77 # Should set env to production here 52 78 # Compile the TypeScript application 53 - env: 54 - NODE_ENV: production 55 79 run: bun build --compile --target bun --sourcemap --format=cjs src/index.ts --outfile=app --external '@libsql/*' --external libsql 56 80 57 - docker: 58 - fromImage: oven/bun:1.3.3 59 - workdir: /app/apps/workflows 60 - copy: 61 - - . /app/ 62 - run: bun run src/build-docker.ts 63 - 81 + # Stage 5: LibSQL dependencies 64 82 libsql: 65 83 fromImage: oven/bun:1.3.3 66 84 workdir: /app/ 85 + labels: 86 + org.opencontainers.image.stage: libsql 67 87 copy: 68 88 - fromBuilder: docker 69 89 source: /app/apps/build-docker/package.json 70 90 target: /app/package.json 71 91 run: bun install 72 92 73 - ca-certs: 74 - fromImage: debian:bullseye-slim 75 - run: apt update && apt install -y ca-certificates && update-ca-certificates 76 - 93 + # Runtime 77 94 fromImage: debian:bullseye-slim 78 95 workdir: /app/ 96 + 97 + # Metadata labels 98 + labels: 99 + org.opencontainers.image.title: OpenStatus Workflows 100 + org.opencontainers.image.description: Background job processing and probe scheduling for OpenStatus 101 + org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus 102 + org.opencontainers.image.vendor: OpenStatus 103 + org.opencontainers.image.authors: OpenStatus Team 104 + 105 + # Copy artifacts from build stages 79 106 copy: 80 107 - fromBuilder: build 81 108 source: /app/apps/workflows/app 82 109 target: /app/apps/workflows/ 83 110 chmod: "555" 111 + - fromBuilder: build 112 + source: /app/data 113 + target: /app/data 84 114 - fromBuilder: libsql 85 115 source: /app/node_modules 86 116 target: /app/packages/db/node_modules ··· 90 120 - fromBuilder: ca-certs 91 121 source: /etc/ssl/certs/ca-certificates.crt 92 122 target: /etc/ssl/certs/ 93 - expose: 3000 123 + 124 + # Install curl for health checks 125 + root: 126 + run: 127 + - apt-get update 128 + - apt-get install -y --no-install-recommends curl 129 + - rm -rf /var/lib/apt/lists/* 130 + 131 + # Security: run as non-root user 132 + user: "1000:1000" 133 + 134 + # Expose port 135 + expose: "3000" 136 + 137 + # Health check 138 + healthcheck: 139 + interval: 30s 140 + timeout: 10s 141 + start: 30s 142 + retries: 3 143 + cmd: curl -f http://localhost:3000/ping || exit 1 144 + 145 + # Start application 94 146 entrypoint: /app/apps/workflows/app
+179 -49
docker-compose.yaml
··· 1 + networks: 2 + openstatus: 3 + driver: bridge 4 + name: openstatus 5 + 6 + volumes: 7 + libsql-data: 8 + name: openstatus-libsql-data 9 + 1 10 services: 2 - internal-server: 11 + # External services 12 + libsql: 13 + container_name: openstatus-libsql 14 + image: ghcr.io/tursodatabase/libsql-server:latest 15 + networks: 16 + - openstatus 17 + ports: 18 + - "8080:8080" 19 + - "5001:5001" 20 + volumes: 21 + - libsql-data:/var/lib/sqld 22 + environment: 23 + - SQLD_NODE=primary 24 + healthcheck: 25 + test: ["CMD-SHELL", "perl -e 'use IO::Socket::INET; exit(IO::Socket::INET->new(PeerAddr=>\"127.0.0.1:8080\",Timeout=>1) ? 0 : 1);'"] 26 + interval: 10s 27 + timeout: 5s 28 + retries: 5 29 + start_period: 10s 30 + restart: unless-stopped 31 + 32 + tinybird-local: 33 + container_name: openstatus-tinybird 34 + image: tinybirdco/tinybird-local:latest 35 + platform: linux/amd64 36 + networks: 37 + - openstatus 38 + ports: 39 + - "7181:7181" 40 + environment: 41 + - COMPATIBILITY_MODE=1 42 + healthcheck: 43 + test: ["CMD", "curl", "-f", "http://localhost:7181/"] 44 + interval: 15s 45 + timeout: 5s 46 + retries: 5 47 + start_period: 20s 48 + restart: unless-stopped 49 + 50 + # Internal Services 51 + workflows: 52 + container_name: openstatus-workflows 3 53 build: 4 - context: . 5 - dockerfile: apps/workflows/Dockerfile 54 + context: . 55 + dockerfile: apps/workflows/Dockerfile 56 + image: openstatus/workflows:latest 57 + networks: 58 + - openstatus 59 + ports: 60 + - "3000:3000" 61 + env_file: 62 + - .env.docker 6 63 environment: 7 64 - DATABASE_URL=http://libsql:8080 8 - - DATABASE_AUTH_TOKEN="basic:token" 9 - - RESEND_API_KEY=test 10 - - UPSTASH_REDIS_REST_URL=test 11 - - UPSTASH_REDIS_REST_TOKEN=test 12 - - NODE_ENV=production 13 - - GCP_PROJECT_ID=test 14 - extra_hosts: 15 - - "host.docker.internal:host-gateway" 65 + - PORT=3000 66 + depends_on: 67 + libsql: 68 + condition: service_healthy 69 + healthcheck: 70 + test: ["CMD-SHELL", "curl -f http://localhost:3000/ping || exit 1"] 71 + interval: 15s 72 + timeout: 10s 73 + retries: 3 74 + start_period: 30s 75 + restart: unless-stopped 16 76 77 + server: 78 + container_name: openstatus-server 79 + build: 80 + context: . 81 + dockerfile: apps/server/Dockerfile 82 + image: openstatus/server:latest 83 + networks: 84 + - openstatus 17 85 ports: 18 - - 3000:3000 19 - volumes: 20 - - type: bind 21 - source: ./data 22 - target: /app/data 23 - image: workflows-test 24 - command: . 25 - libsql: 26 - image: ghcr.io/tursodatabase/libsql-server:latest 86 + - "3001:3000" 87 + env_file: 88 + - .env.docker 89 + environment: 90 + - DATABASE_URL=http://libsql:8080 91 + - PORT=3000 92 + depends_on: 93 + libsql: 94 + condition: service_healthy 95 + healthcheck: 96 + test: ["CMD-SHELL", "curl -f http://localhost:3000/ping || exit 1"] 97 + interval: 15s 98 + timeout: 10s 99 + retries: 3 100 + start_period: 30s 101 + restart: unless-stopped 102 + 103 + private-location: 104 + container_name: openstatus-private-location 105 + build: 106 + context: apps/private-location 107 + dockerfile: Dockerfile 108 + image: openstatus/private-location:latest 109 + networks: 110 + - openstatus 27 111 ports: 28 - - 8080:8080 29 - - 5001:5001 30 - api: 112 + - "8081:8080" 113 + env_file: 114 + - .env.docker 115 + environment: 116 + - DB_URL=http://libsql:8080 117 + - TINYBIRD_URL=http://tinybird-local:7181 118 + - GIN_MODE=release 119 + - PORT=8080 120 + depends_on: 121 + server: 122 + condition: service_started 123 + healthcheck: 124 + test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"] 125 + interval: 15s 126 + timeout: 10s 127 + retries: 3 128 + start_period: 30s 129 + restart: unless-stopped 130 + 131 + dashboard: 132 + container_name: openstatus-dashboard 31 133 build: 32 - context: . 33 - dockerfile: apps/server/Dockerfile 134 + context: . 135 + dockerfile: apps/dashboard/Dockerfile 136 + image: openstatus/dashboard:latest 137 + networks: 138 + - openstatus 34 139 ports: 35 - - 3001:3000 36 - image: server 140 + - "3002:3000" 141 + env_file: 142 + - .env.docker 37 143 environment: 38 - - DATABASE_URL=http://libsql-1:8080 39 - - DATABASE_AUTH_TOKEN="basic:token" 40 - - UPSTASH_REDIS_REST_URL=test 41 - - UPSTASH_REDIS_REST_TOKEN=test 42 - - NODE_ENV=production 43 - - NEXT_PUBLIC_OPENPANEL_CLIENT_ID=test 44 - - OPENPANEL_CLIENT_SECRET=test 45 - - RESEND_API_KEY=test 46 - command: . 47 - # dashboard: 48 - # build: 49 - # context: . 50 - # dockerfile: apps/dashboard/Dockerfile 51 - # ports: 52 - # - 3002:3000 53 - # image: dashboard 54 - # environment: 55 - # - API_URL=http://host.docker.internal:3001 56 - # - NEXT_PUBLIC_OPENPANEL_CLIENT_ID=test 57 - # - OPENPANEL_CLIENT_SECRET=test 58 - # - NODE_ENV=production 59 - # command: . 144 + - DATABASE_URL=http://libsql:8080 145 + - PORT=3000 146 + - HOSTNAME=0.0.0.0 147 + - AUTH_TRUST_HOST=true 148 + depends_on: 149 + libsql: 150 + condition: service_healthy 151 + server: 152 + condition: service_started 153 + healthcheck: 154 + test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"] 155 + interval: 15s 156 + timeout: 10s 157 + retries: 3 158 + start_period: 45s 159 + restart: unless-stopped 160 + 161 + status-page: 162 + container_name: openstatus-status-page 163 + build: 164 + context: . 165 + dockerfile: apps/status-page/Dockerfile 166 + image: openstatus/status-page:latest 167 + networks: 168 + - openstatus 169 + ports: 170 + - "3003:3000" 171 + env_file: 172 + - .env.docker 173 + environment: 174 + - DATABASE_URL=http://libsql:8080 175 + - PORT=3000 176 + - HOSTNAME=0.0.0.0 177 + - AUTH_TRUST_HOST=true 178 + depends_on: 179 + libsql: 180 + condition: service_healthy 181 + server: 182 + condition: service_started 183 + healthcheck: 184 + test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"] 185 + interval: 15s 186 + timeout: 10s 187 + retries: 3 188 + start_period: 45s 189 + restart: unless-stopped
-25
packages/tinybird/_legacy/aggregated_monitor_day_mv.pipe
··· 1 - # REMINDER: legacy 2 - 3 - VERSION 0 4 - 5 - NODE aggregated_monitor_day_0 6 - SQL > 7 - 8 - SELECT 9 - toUnixTimestamp64Milli(toDateTime64(DATE(cronTimestamp / 1000), 3)) AS day, 10 - monitorId, 11 - avgState(latency) AS avgLatency, 12 - countState() AS count, 13 - countState(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok 14 - FROM ping_response__v4 15 - WHERE (day IS NOT NULL) AND (day != 0) 16 - GROUP BY 17 - day, 18 - monitorId 19 - ORDER BY day DESC 20 - 21 - TYPE materialized 22 - DATASOURCE aggregated_monitor_per_day_0_mv 23 - ENGINE "AggregatingMergeTree" 24 - ENGINE_SORTING_KEY "day, monitorId" 25 -
-14
packages/tinybird/_legacy/aggregated_monitor_per_day_mv.datasource
··· 1 - # REMINDER: legacy 2 - 3 - # Data Source created from Pipe 'aggregated_monitor_day_mv' 4 - VERSION 0 5 - 6 - SCHEMA > 7 - `day` Int64, 8 - `monitorId` String, 9 - `avgLatency` AggregateFunction(avg, Int16), 10 - `count` AggregateFunction(count), 11 - `ok` AggregateFunction(count, Nullable(UInt8)) 12 - 13 - ENGINE "AggregatingMergeTree" 14 - ENGINE_SORTING_KEY "day, monitorId"
-20
packages/tinybird/_migration/ping_response.datasource
··· 1 - VERSION 8 2 - 3 - SCHEMA > 4 - `latency` Int64 `json:$.latency`, 5 - `monitorId` String `json:$.monitorId`, 6 - `region` LowCardinality(String) `json:$.region`, 7 - `statusCode` Nullable(Int16) `json:$.statusCode`, 8 - `error` Int8 `json:$.error`, 9 - `timestamp` Int64 `json:$.timestamp`, 10 - `url` String `json:$.url`, 11 - `workspaceId` String `json:$.workspaceId`, 12 - `cronTimestamp` Int64 `json:$.cronTimestamp`, 13 - `message` Nullable(String) `json:$.message`, 14 - `timing` Nullable(String) `json:$.timing`, 15 - `headers` Nullable(String) `json:$.headers`, 16 - `assertions` Nullable(String) `json:$.assertions` 17 - 18 - ENGINE "MergeTree" 19 - ENGINE_SORTING_KEY "monitorId, cronTimestamp" 20 - ENGINE_PARTITION_KEY "toYYYYMM(fromUnixTimestamp64Milli(cronTimestamp))"
-21
packages/tinybird/_migration/tb_backfill_populate.pipe
··· 1 - NODE mat_node 2 - SQL > 3 - 4 - SELECT 5 - latency, 6 - monitorId, 7 - toLowCardinality(region) region, 8 - statusCode, 9 - if(statusCode >= 200 AND statusCode < 300, toInt8(0), toInt8(1)) AS error, 10 - timestamp, 11 - url, 12 - workspaceId, 13 - cronTimestamp, 14 - message, 15 - timing, 16 - headers, 17 - FROM ping_response__v7 18 - WHERE fromUnixTimestamp64Milli(cronTimestamp) <= '2024-03-29 13:16:00.000' 19 - 20 - TYPE materialized 21 - DATASOURCE ping_response__v8
-6
packages/tinybird/_migration/tb_datasource_union.pipe
··· 1 - NODE union_all 2 - SQL > 3 - 4 - SELECT count() FROM ping_response__v8 5 - UNION ALL 6 - SELECT count() FROM ping_response__v7
-21
packages/tinybird/_migration/tb_materialize_until_change_ingest.pipe
··· 1 - NODE mat_node 2 - SQL > 3 - 4 - SELECT 5 - latency, 6 - monitorId, 7 - toLowCardinality(region) region, 8 - statusCode, 9 - if(statusCode >= 200 AND statusCode < 300, toInt8(0), toInt8(1)) AS error, 10 - timestamp, 11 - url, 12 - workspaceId, 13 - cronTimestamp, 14 - message, 15 - timing, 16 - headers, 17 - FROM ping_response__v7 18 - WHERE fromUnixTimestamp64Milli(cronTimestamp) > '2023-03-29 13:16:00.000' 19 - 20 - TYPE materialized 21 - DATASOURCE ping_response__v8
packages/tinybird/datasources/audit_log.datasource packages/tinybird/datasources/audit_log__v0.datasource
+19
packages/tinybird/datasources/mv__http_14d__v0.datasource
··· 1 + VERSION 0 2 + # Data Source created from Pipe 'aggregate__http_14d__v0' 3 + 4 + SCHEMA > 5 + `time` DateTime, 6 + `latency` Int64, 7 + `error` Int8, 8 + `region` LowCardinality(String), 9 + `trigger` Nullable(String), 10 + `statusCode` Nullable(Int16), 11 + `timestamp` Int64, 12 + `cronTimestamp` Int64, 13 + `monitorId` String, 14 + `workspaceId` String 15 + 16 + ENGINE "MergeTree" 17 + ENGINE_PARTITION_KEY "toYYYYMM(time)" 18 + ENGINE_SORTING_KEY "monitorId, time" 19 + ENGINE_TTL "time + toIntervalDay(14)"
packages/tinybird/datasources/mv__http_1d.datasource packages/tinybird/datasources/mv__http_1d__v0.datasource
packages/tinybird/datasources/mv__http_30d.datasource packages/tinybird/datasources/mv__http_30d__v0.datasource
packages/tinybird/datasources/mv__http_7d.datasource packages/tinybird/datasources/mv__http_7d__v0.datasource
+3 -1
packages/tinybird/datasources/mv__http_full_14d__v0.datasource
··· 18 18 `body` Nullable(String), 19 19 `trigger` Nullable(String), 20 20 `id` Nullable(String), 21 - `requestStatus` Nullable(String) 21 + `requestStatus` Nullable(String), 22 + `method` Nullable(String) 23 + 22 24 23 25 ENGINE "MergeTree" 24 26 ENGINE_PARTITION_KEY "toYYYYMM(time)"
+5 -2
packages/tinybird/datasources/mv__http_full_30d.datasource packages/tinybird/datasources/mv__http_full_30d__v0.datasource
··· 1 - VERSION 0 2 1 # Data Source created from Pipe 'aggregate__http_full_30d__v0' 3 2 4 3 SCHEMA > ··· 17 16 `headers` Nullable(String), 18 17 `assertions` Nullable(String), 19 18 `body` Nullable(String), 20 - `trigger` Nullable(String) 19 + `trigger` Nullable(String), 20 + `id` Nullable(String), 21 + `requestStatus` Nullable(String), 22 + `method` Nullable(String) 23 + 21 24 22 25 ENGINE "MergeTree" 23 26 ENGINE_PARTITION_KEY "toYYYYMM(time)"
+14
packages/tinybird/datasources/mv__http_status_14d__v0.datasource
··· 1 + # Data Source created from Pipe 'aggregate__http_status_14d__v0' 2 + 3 + SCHEMA > 4 + `time` DateTime('UTC'), 5 + `monitorId` String, 6 + `total` AggregateFunction(count), 7 + `success` AggregateFunction(count, Nullable(UInt8)), 8 + `degraded` AggregateFunction(count, Nullable(UInt8)), 9 + `error` AggregateFunction(count, Nullable(UInt8)) 10 + 11 + ENGINE "AggregatingMergeTree" 12 + ENGINE_PARTITION_KEY "toYYYYMM(time)" 13 + ENGINE_SORTING_KEY "monitorId, time" 14 + ENGINE_TTL "time + toIntervalDay(14)"
packages/tinybird/datasources/mv__http_status_45d.datasource packages/tinybird/datasources/mv__http_status_45d__v0.datasource
packages/tinybird/datasources/mv__http_status_7d.datasource packages/tinybird/datasources/mv__http_status_7d__v0.datasource
+21
packages/tinybird/datasources/mv__http_timing_phases_14d__v1.datasource
··· 1 + # Data Source created from Pipe 'aggregate__http_timing_phases_14d__v1' 2 + 3 + SCHEMA > 4 + `time` DateTime, 5 + `latency` Int64, 6 + `region` LowCardinality(String), 7 + `trigger` Nullable(String), 8 + `statusCode` Nullable(Int16), 9 + `monitorId` String, 10 + `workspaceId` String, 11 + `requestStatus` Nullable(String), 12 + `dns` Nullable(Int64), 13 + `connect` Nullable(Int64), 14 + `tls` Nullable(Int64), 15 + `firstByte` Nullable(Int64), 16 + `transfer` Nullable(Int64) 17 + 18 + ENGINE "MergeTree" 19 + ENGINE_PARTITION_KEY "toYYYYMM(time)" 20 + ENGINE_SORTING_KEY "monitorId, time" 21 + ENGINE_TTL "time + toIntervalDay(14)"
-1
packages/tinybird/datasources/mv__http_uptime_30d.datasource packages/tinybird/datasources/mv__http_uptime_30d__v1.datasource
··· 1 1 # Data Source created from Pipe 'aggregate__http_uptime_30d__v1' 2 - VERSION 1 3 2 4 3 SCHEMA > 5 4 `time` DateTime,
packages/tinybird/datasources/mv__tcp_14d.datasource packages/tinybird/datasources/mv__tcp_14d__v0.datasource
packages/tinybird/datasources/mv__tcp_1d.datasource packages/tinybird/datasources/mv__tcp_1d__v0.datasource
packages/tinybird/datasources/mv__tcp_30d.datasource packages/tinybird/datasources/mv__tcp_30d__v0.datasource
packages/tinybird/datasources/mv__tcp_7d.datasource packages/tinybird/datasources/mv__tcp_7d__v0.datasource
+3 -2
packages/tinybird/datasources/mv__tcp_full_30d.datasource packages/tinybird/datasources/mv__tcp_full_30d__v0.datasource
··· 1 - VERSION 0 2 1 # Data Source created from Pipe 'aggregate__tcp_full_30d__v0' 3 2 4 3 SCHEMA > ··· 13 12 `errorMessage` Nullable(String), 14 13 `error` Int16, 15 14 `trigger` Nullable(String), 16 - `uri` Nullable(String) 15 + `uri` Nullable(String), 16 + `id` Nullable(String), 17 + `requestStatus` Nullable(String) 17 18 18 19 ENGINE "MergeTree" 19 20 ENGINE_PARTITION_KEY "toYYYYMM(time)"
packages/tinybird/datasources/mv__tcp_status_45d.datasource packages/tinybird/datasources/mv__tcp_status_45d__v0.datasource
packages/tinybird/datasources/mv__tcp_status_7d.datasource packages/tinybird/datasources/mv__tcp_status_7d__v0.datasource
+4 -2
packages/tinybird/datasources/ping_response.datasource packages/tinybird/datasources/ping_response__v8.datasource
··· 1 - VERSION 8 2 1 3 2 SCHEMA > 4 3 `latency` Int64 `json:$.latency`, ··· 16 15 `assertions` Nullable(String) `json:$.assertions`, 17 16 `body` Nullable(String) `json:$.body`, 18 17 `trigger` Nullable(String) `json:$.trigger`, 18 + `id` Nullable(String) `json:$.id`, 19 + `requestStatus` Nullable(String) `json:$.requestStatus`, 20 + `method` String `json:$.method` 19 21 20 22 ENGINE "MergeTree" 23 + ENGINE_PARTITION_KEY "toYYYYMM(fromUnixTimestamp64Milli(cronTimestamp))" 21 24 ENGINE_SORTING_KEY "monitorId, cronTimestamp" 22 - ENGINE_PARTITION_KEY "toYYYYMM(fromUnixTimestamp64Milli(cronTimestamp))"
+19
packages/tinybird/datasources/tcp_response__v0.datasource
··· 1 + 2 + SCHEMA > 3 + `monitorId` Int32 `json:$.monitorId`, 4 + `region` String `json:$.region`, 5 + `timestamp` Int64 `json:$.timestamp`, 6 + `cronTimestamp` Int64 `json:$.timestamp`, 7 + `timing` String `json:$.timing`, 8 + `workspaceId` Int32 `json:$.workspaceId`, 9 + `latency` Int64 `json:$.latency`, 10 + `errorMessage` Nullable(String) `json:$.errorMessage`, 11 + `error` Int16 `json:$.error`, 12 + `trigger` Nullable(String) `json:$.trigger`, 13 + `uri` Nullable(String) `json:$.uri`, 14 + `id` Nullable(String) `json:$.id`, 15 + `requestStatus` Nullable(String) `json:$.requestStatus` 16 + 17 + ENGINE "MergeTree" 18 + ENGINE_PARTITION_KEY "toYYYYMM(fromUnixTimestamp64Milli(timestamp))" 19 + ENGINE_SORTING_KEY "monitorId, workspaceId"
-25
packages/tinybird/datasources/web_vitals.datasource
··· 1 - VERSION 0 2 - 3 - SCHEMA > 4 - `browser` String `json:$.browser`, 5 - `city` String `json:$.city`, 6 - `continent` String `json:$.continent`, 7 - `country` String `json:$.country`, 8 - `device` String `json:$.device`, 9 - `dsn` String `json:$.dsn`, 10 - `href` String `json:$.href`, 11 - `id` String `json:$.id`, 12 - `name` String `json:$.name`, 13 - `os` String `json:$.os`, 14 - `path` String `json:$.path`, 15 - `rating` String `json:$.rating`, 16 - `region_code` String `json:$.region_code`, 17 - `screen` String `json:$.screen`, 18 - `session_id` String `json:$.session_id`, 19 - `speed` String `json:$.speed`, 20 - `timezone` String `json:$.timezone`, 21 - `value` Float32 `json:$.value`, 22 - `timestamp` Nullable(DateTime) `json:$.timestamp` 23 - 24 - ENGINE "MergeTree" 25 - ENGINE_SORTING_KEY "session_id, speed, timezone"
+17
packages/tinybird/endpoints/endpoint__audit_log.pipe
··· 1 + VERSION 1 2 + 3 + TAGS endpoint 4 + 5 + 6 + NODE endpoint 7 + SQL > 8 + 9 + % 10 + SELECT action, id, metadata, timestamp 11 + FROM audit_log__v0 12 + WHERE 13 + id = {{String(monitorId, 'monitor:1', required=True)}} 14 + AND timestamp > toUnixTimestamp(now() - INTERVAL {{ Int64(interval, 30) }} day) * 1000 15 + ORDER BY timestamp DESC 16 + 17 + TYPE ENDPOINT
+1 -3
packages/tinybird/pipes/__ttl_45d_count_utc_get.pipe
··· 6 6 7 7 % 8 8 SELECT time as day, countMerge(count) as count, countMerge(ok) as ok 9 - FROM mv__http_status_45d 9 + FROM mv__http_status_45d__v0 10 10 WHERE 11 11 monitorId = {{ String(monitorId, '4') }} 12 12 GROUP BY day ··· 17 17 TO toStartOfDay( 18 18 date_sub(DAY, 45, now()) 19 19 ) STEP INTERVAL -1 DAY 20 - 21 -
-28
packages/tinybird/pipes/aggregate__http_14d.pipe
··· 1 - VERSION 0 2 - 3 - TAGS http 4 - 5 - NODE aggregate 6 - SQL > 7 - 8 - SELECT 9 - toDateTime(fromUnixTimestamp64Milli(cronTimestamp)) AS time, 10 - latency, 11 - error, 12 - region, 13 - trigger, 14 - statusCode, 15 - timestamp, 16 - cronTimestamp, 17 - monitorId, 18 - workspaceId 19 - FROM ping_response__v8 20 - 21 - TYPE materialized 22 - DATASOURCE mv__http_14d__v0 23 - ENGINE "MergeTree" 24 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 25 - ENGINE_SORTING_KEY "monitorId, time" 26 - ENGINE_TTL "time + toIntervalDay(14)" 27 - 28 -
-23
packages/tinybird/pipes/aggregate__http_1d.pipe
··· 1 - TAGS "http" 2 - 3 - NODE aggregate 4 - SQL > 5 - 6 - SELECT 7 - toDateTime(fromUnixTimestamp64Milli(cronTimestamp)) AS time, 8 - latency, 9 - error, 10 - requestStatus, 11 - region, 12 - trigger, 13 - statusCode, 14 - timestamp, 15 - cronTimestamp, 16 - monitorId, 17 - workspaceId 18 - FROM ping_response__v8 19 - 20 - TYPE materialized 21 - DATASOURCE mv__http_1d__v1 22 - 23 -
-28
packages/tinybird/pipes/aggregate__http_30d.pipe
··· 1 - VERSION 0 2 - 3 - TAGS http 4 - 5 - NODE aggregate 6 - SQL > 7 - 8 - SELECT 9 - toDateTime(fromUnixTimestamp64Milli(cronTimestamp)) AS time, 10 - latency, 11 - error, 12 - region, 13 - trigger, 14 - statusCode, 15 - timestamp, 16 - cronTimestamp, 17 - monitorId, 18 - workspaceId 19 - FROM ping_response__v8 20 - 21 - TYPE materialized 22 - DATASOURCE mv__http_30d__v0 23 - ENGINE "MergeTree" 24 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 25 - ENGINE_SORTING_KEY "monitorId, time" 26 - ENGINE_TTL "time + toIntervalDay(30)" 27 - 28 -
-28
packages/tinybird/pipes/aggregate__http_7d.pipe
··· 1 - VERSION 0 2 - 3 - TAGS http 4 - 5 - NODE aggregate 6 - SQL > 7 - 8 - SELECT 9 - toDateTime(fromUnixTimestamp64Milli(cronTimestamp)) AS time, 10 - latency, 11 - error, 12 - region, 13 - trigger, 14 - statusCode, 15 - timestamp, 16 - cronTimestamp, 17 - monitorId, 18 - workspaceId 19 - FROM ping_response__v8 20 - 21 - TYPE materialized 22 - DATASOURCE mv__http_7d__v0 23 - ENGINE "MergeTree" 24 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 25 - ENGINE_SORTING_KEY "monitorId, time" 26 - ENGINE_TTL "time + toIntervalDay(7)" 27 - 28 -
+1 -6
packages/tinybird/pipes/aggregate__http_full_30d.pipe packages/tinybird/pipes/aggregate__http_full_30d__v0.pipe
··· 1 - VERSION 0 2 1 DESCRIPTION > 3 2 Stores all the data from the http_response table for the last 30 days, mainly used for accessing the data details. 4 3 5 4 6 - TAGS http, full 5 + TAGS "http, full" 7 6 8 7 NODE aggregate 9 8 SQL > ··· 15 14 16 15 TYPE materialized 17 16 DATASOURCE mv__http_full_30d__v0 18 - ENGINE "MergeTree" 19 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 20 - ENGINE_SORTING_KEY "monitorId, time" 21 - ENGINE_TTL "time + toIntervalDay(30)" 22 17 23 18
+1 -3
packages/tinybird/pipes/aggregate__http_status_14d.pipe
··· 22 22 monitorId 23 23 24 24 TYPE materialized 25 - DATASOURCE mv_http_status_14d__v0 26 - 27 - 25 + DATASOURCE mv__http_status_14d__v0
-5
packages/tinybird/pipes/aggregate__http_status_45d.pipe
··· 17 17 18 18 TYPE materialized 19 19 DATASOURCE mv__http_status_45d__v0 20 - ENGINE "AggregatingMergeTree" 21 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 22 - ENGINE_SORTING_KEY "monitorId, time" 23 - ENGINE_TTL "time + toIntervalDay(45)" 24 -
-5
packages/tinybird/pipes/aggregate__http_status_7d.pipe
··· 17 17 18 18 TYPE materialized 19 19 DATASOURCE mv__http_status_7d__v0 20 - ENGINE "AggregatingMergeTree" 21 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 22 - ENGINE_SORTING_KEY "monitorId, time" 23 - ENGINE_TTL "time + toIntervalDay(7)" 24 -
-6
packages/tinybird/pipes/aggregate__tcp_14d.pipe
··· 19 19 20 20 TYPE materialized 21 21 DATASOURCE mv__tcp_14d__v0 22 - ENGINE "MergeTree" 23 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 24 - ENGINE_SORTING_KEY "monitorId, time" 25 - ENGINE_TTL "time + toIntervalDay(14)" 26 - 27 -
-6
packages/tinybird/pipes/aggregate__tcp_1d.pipe
··· 19 19 20 20 TYPE materialized 21 21 DATASOURCE mv__tcp_1d__v0 22 - ENGINE "MergeTree" 23 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 24 - ENGINE_SORTING_KEY "monitorId, time" 25 - ENGINE_TTL "time + toIntervalDay(1)" 26 - 27 -
-6
packages/tinybird/pipes/aggregate__tcp_30d.pipe
··· 19 19 20 20 TYPE materialized 21 21 DATASOURCE mv__tcp_30d__v0 22 - ENGINE "MergeTree" 23 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 24 - ENGINE_SORTING_KEY "monitorId, time" 25 - ENGINE_TTL "time + toIntervalDay(30)" 26 - 27 -
-6
packages/tinybird/pipes/aggregate__tcp_7d.pipe
··· 19 19 20 20 TYPE materialized 21 21 DATASOURCE mv__tcp_7d__v0 22 - ENGINE "MergeTree" 23 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 24 - ENGINE_SORTING_KEY "monitorId, time" 25 - ENGINE_TTL "time + toIntervalDay(7)" 26 - 27 -
-23
packages/tinybird/pipes/aggregate__tcp_full_30d.pipe
··· 1 - VERSION 0 2 - DESCRIPTION > 3 - Stores all the data from the http_response table for the last 30 days, mainly used for accessing the data details. 4 - 5 - 6 - TAGS tcp, full 7 - 8 - NODE aggregate 9 - SQL > 10 - 11 - SELECT 12 - toDateTime(fromUnixTimestamp64Milli(cronTimestamp)) AS time, 13 - * 14 - FROM tcp_response__v0 15 - 16 - TYPE materialized 17 - DATASOURCE mv__tcp_full_30d__v0 18 - ENGINE "MergeTree" 19 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 20 - ENGINE_SORTING_KEY "monitorId, time" 21 - ENGINE_TTL "time + toIntervalDay(30)" 22 - 23 -
+16
packages/tinybird/pipes/aggregate__tcp_full_30d__v0.pipe
··· 1 + DESCRIPTION > 2 + Stores all the data from the http_response table for the last 30 days, mainly used for accessing the data details. 3 + 4 + 5 + TAGS "tcp, full" 6 + 7 + NODE aggregate 8 + SQL > 9 + 10 + SELECT 11 + toDateTime(fromUnixTimestamp64Milli(tcp_response__v0.cronTimestamp)) AS time, 12 + * 13 + FROM tcp_response__v0 14 + 15 + TYPE materialized 16 + DATASOURCE mv__tcp_full_30d__v0
-5
packages/tinybird/pipes/aggregate__tcp_status_45d.pipe
··· 17 17 18 18 TYPE materialized 19 19 DATASOURCE mv__tcp_status_45d__v0 20 - ENGINE "AggregatingMergeTree" 21 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 22 - ENGINE_SORTING_KEY "monitorId, time" 23 - ENGINE_TTL "time + toIntervalDay(45)" 24 -
-5
packages/tinybird/pipes/aggregate__tcp_status_7d.pipe
··· 17 17 18 18 TYPE materialized 19 19 DATASOURCE mv__tcp_status_7d__v0 20 - ENGINE "AggregatingMergeTree" 21 - ENGINE_PARTITION_KEY "toYYYYMM(time)" 22 - ENGINE_SORTING_KEY "monitorId, time" 23 - ENGINE_TTL "time + toIntervalDay(7)" 24 -
+1
packages/tinybird/pipes/endpoint__audit_log.pipe packages/tinybird/endpoints/endpoint__audit_log__v1.pipe
··· 14 14 AND timestamp > toUnixTimestamp(now() - INTERVAL {{ Int64(interval, 30) }} day) * 1000 15 15 ORDER BY timestamp DESC 16 16 17 + TYPE ENDPOINT 17 18
+1
packages/tinybird/pipes/endpoint__dns_get_14d__v0.pipe packages/tinybird/endpoints/endpoint__dns_get_14d__v0.pipe
··· 12 12 ORDER BY timestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_list_14d__v0.pipe packages/tinybird/endpoints/endpoint__dns_list_14d__v0.pipe
··· 13 13 ORDER BY timestamp DESC 14 14 15 15 16 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_14d__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_14d__v0.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_1d__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_1d__v0.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_7d__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_7d__v0.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_latency_1d_multi__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_latency_1d_multi__v0.pipe
··· 23 23 ORDER BY h ASC 24 24 25 25 26 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_latency_7d__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_latency_7d__v0.pipe
··· 27 27 ORDER BY h DESC 28 28 29 29 30 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_metrics_regions_14d__v0.pipe packages/tinybird/endpoints/endpoint__dns_metrics_regions_14d__v0.pipe
··· 23 23 ORDER BY h DESC 24 24 25 25 26 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_status_45d__v0.pipe packages/tinybird/endpoints/endpoint__dns_status_45d__v0.pipe
··· 17 17 ORDER BY day DESC 18 18 19 19 20 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__dns_uptime_30d__v0.pipe packages/tinybird/endpoints/endpoint__dns_uptime_30d__v0.pipe
··· 25 25 ORDER BY interval DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_get_14d__v0.pipe packages/tinybird/endpoints/endpoint__http_get_14d__v0.pipe
··· 12 12 ORDER BY time DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_get_30d.pipe packages/tinybird/endpoints/endpoint__http_get_30d.pipe
··· 16 16 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_14d.pipe packages/tinybird/endpoints/endpoint__http_list_14d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_14d__v1.pipe packages/tinybird/endpoints/endpoint__http_list_14d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_1d.pipe packages/tinybird/endpoints/endpoint__http_list_1d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_1d__v1.pipe packages/tinybird/endpoints/endpoint__http_list_1d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_7d.pipe packages/tinybird/endpoints/endpoint__http_list_7d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_list_7d__v1.pipe packages/tinybird/endpoints/endpoint__http_list_7d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_14d.pipe packages/tinybird/endpoints/endpoint__http_metrics_14d.pipe
··· 36 36 AND time < toDateTime64(now() - INTERVAL 14 DAY, 3) 37 37 38 38 39 + TYPE ENDPOINT
+1 -1
packages/tinybird/pipes/endpoint__http_metrics_14d__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_14d__v1.pipe
··· 39 39 AND time < toDateTime64(now() - INTERVAL 14 DAY, 3) 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 - 42 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_1d.pipe packages/tinybird/endpoints/endpoint__http_metrics_1d.pipe
··· 36 36 AND time < toDateTime64(now() - INTERVAL 1 DAY, 3) 37 37 38 38 39 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_1d__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_1d__v1.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_7d.pipe packages/tinybird/endpoints/endpoint__http_metrics_7d.pipe
··· 36 36 AND time < toDateTime64(now() - INTERVAL 7 DAY, 3) 37 37 38 38 39 + TYPE ENDPOINT
+1 -1
packages/tinybird/pipes/endpoint__http_metrics_7d__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_7d__v1.pipe
··· 39 39 AND time < toDateTime64(now() - INTERVAL 7 DAY, 3) 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 - 42 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_interval_14d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_interval_14d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_interval_1d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_interval_1d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_interval_7d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_interval_7d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_region_14d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_region_14d.pipe
··· 25 25 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_region_1d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_region_1d.pipe
··· 22 22 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_by_region_7d.pipe packages/tinybird/endpoints/endpoint__http_metrics_by_region_7d.pipe
··· 22 22 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_global_1d.pipe packages/tinybird/endpoints/endpoint__http_metrics_global_1d__v0.pipe
··· 22 22 GROUP BY monitorId 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_latency_1d__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_latency_1d__v1.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_latency_1d_multi__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_latency_1d_multi__v1.pipe
··· 22 22 ORDER BY h ASC 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_latency_7d__v1.pipe packages/tinybird/endpoints/endpoint__http_metrics_latency_7d__v1.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_regions_14d__v0.pipe packages/tinybird/endpoints/endpoint__http_metrics_regions_14d__v0.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_regions_1d__v0.pipe packages/tinybird/endpoints/endpoint__http_metrics_regions_1d__v0.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_metrics_regions_7d__v0.pipe packages/tinybird/endpoints/endpoint__http_metrics_regions_7d__v0.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_status_14d.pipe packages/tinybird/endpoints/endpoint__http_status_14d.pipe
··· 23 23 LIMIT {{ Int16(days, 14) }} 24 24 25 25 26 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_status_45d.pipe packages/tinybird/endpoints/endpoint__http_status_45d.pipe
··· 21 21 LIMIT {{ Int16(days, 45) }} 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_status_45d__v1.pipe packages/tinybird/endpoints/endpoint__http_status_45d__v1.pipe
··· 17 17 ORDER BY day DESC 18 18 19 19 20 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_status_7d.pipe packages/tinybird/endpoints/endpoint__http_status_7d.pipe
··· 20 20 ) STEP INTERVAL -1 DAY 21 21 22 22 23 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_timing_phases_14d__v1.pipe packages/tinybird/endpoints/endpoint__http_timing_phases_14d__v1.pipe
··· 36 36 ORDER BY h ASC 37 37 38 38 39 + TYPE ENDPOINT
+7 -6
packages/tinybird/pipes/endpoint__http_uptime_30d.pipe packages/tinybird/endpoints/endpoint__http_uptime_30d__v1.pipe
··· 1 - VERSION 1 2 - 3 1 TAGS "http" 4 2 5 3 NODE endpoint 6 4 SQL > 7 5 8 6 % 9 - SELECT 7 + SELECT 10 8 toStartOfInterval(time, INTERVAL {{ String(interval, '30', required=True) }} minute) AS interval, 11 9 countIf(requestStatus = 'success') AS success, 12 10 countIf(requestStatus = 'degraded') AS degraded, ··· 14 12 FROM mv__http_uptime_30d__v1 15 13 WHERE 16 14 monitorId = {{ String(monitorId, '1', required=True) }} 17 - {% if fromDate %} AND time >= toDateTime({{ String(fromDate) }}) {% end %} 18 - {% if toDate %} AND time <= toDateTime({{ String(toDate) }}) {% end %} 15 + {% if fromDate %} AND time >= parseDateTimeBestEffortOrNull({{ String(fromDate) }}) {% end %} 16 + {% if toDate %} AND time <= parseDateTimeBestEffortOrNull({{ String(toDate) }}) {% end %} 19 17 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 20 18 GROUP BY interval 21 - ORDER BY interval DESC 19 + ORDER BY interval DESC 20 + 21 + 22 + TYPE ENDPOINT
+2 -1
packages/tinybird/pipes/endpoint__http_uptime_7d__v1.pipe packages/tinybird/endpoints/endpoint__http_uptime_7d__v1.pipe
··· 4 4 SQL > 5 5 6 6 % 7 - SELECT 7 + SELECT 8 8 toStartOfInterval(time, INTERVAL {{ String(interval, '30', required=True) }} minute) AS interval, 9 9 countIf(requestStatus = 'success') AS success, 10 10 countIf(requestStatus = 'degraded') AS degraded, ··· 19 19 ORDER BY interval DESC 20 20 21 21 22 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__http_workspace_30d__v0.pipe packages/tinybird/endpoints/endpoint__http_workspace_30d__v0.pipe
··· 13 13 ORDER BY day DESC 14 14 15 15 16 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__stats_global.pipe packages/tinybird/endpoints/endpoint__stats_global.pipe
··· 22 22 {% end %} 23 23 {% end %} 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_get_14d__v0.pipe packages/tinybird/endpoints/endpoint__tcp_get_14d__v0.pipe
··· 12 12 ORDER BY time DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_get_30d.pipe packages/tinybird/endpoints/endpoint__tcp_get_30d.pipe
··· 16 16 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_14d.pipe packages/tinybird/endpoints/endpoint__tcp_list_14d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_14d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_list_14d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_1d.pipe packages/tinybird/endpoints/endpoint__tcp_list_1d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_1d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_list_1d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_7d.pipe packages/tinybird/endpoints/endpoint__tcp_list_7d.pipe
··· 12 12 ORDER BY cronTimestamp DESC 13 13 14 14 15 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_list_7d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_list_7d__v1.pipe
··· 16 16 ORDER BY time DESC 17 17 18 18 19 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_14d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_14d.pipe
··· 39 39 AND time < toDateTime64(now() - INTERVAL 14 DAY, 3) 40 40 41 41 42 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_14d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_14d__v1.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_1d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_1d.pipe
··· 36 36 AND time < toDateTime64(now() - INTERVAL 1 DAY, 3) 37 37 38 38 39 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_1d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_1d__v1.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_7d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_7d.pipe
··· 36 36 AND time < toDateTime64(now() - INTERVAL 7 DAY, 3) 37 37 38 38 39 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_7d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_7d__v1.pipe
··· 40 40 {% if regions %} AND region IN {{ Array(regions, 'String', 'ams,fra') }} {% end %} 41 41 42 42 43 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_interval_14d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_interval_14d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_interval_1d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_interval_1d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_interval_7d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_interval_7d.pipe
··· 25 25 ORDER BY h DESC 26 26 27 27 28 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_region_14d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_region_14d.pipe
··· 22 22 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_region_1d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_region_1d.pipe
··· 22 22 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_by_region_7d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_by_region_7d.pipe
··· 22 22 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_global_1d.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_global_1d.pipe
··· 22 22 GROUP BY monitorId 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_latency_1d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_latency_1d__v1.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_latency_1d_multi__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_latency_1d_multi__v1.pipe
··· 22 22 ORDER BY h DESC 23 23 24 24 25 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_metrics_latency_7d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_metrics_latency_7d__v1.pipe
··· 21 21 ORDER BY h DESC 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_status_45d.pipe packages/tinybird/endpoints/endpoint__tcp_status_45d.pipe
··· 21 21 LIMIT {{ Int16(days, 45) }} 22 22 23 23 24 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_status_45d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_status_45d__v1.pipe
··· 17 17 ORDER BY day DESC 18 18 19 19 20 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_status_7d.pipe packages/tinybird/endpoints/endpoint__tcp_status_7d.pipe
··· 20 20 ) STEP INTERVAL -1 DAY 21 21 22 22 23 + TYPE ENDPOINT
+2 -1
packages/tinybird/pipes/endpoint__tcp_uptime_30d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_uptime_30d__v1.pipe
··· 4 4 SQL > 5 5 6 6 % 7 - SELECT 7 + SELECT 8 8 toStartOfInterval(time, INTERVAL {{ String(interval, '30', required=True) }} minute) AS interval, 9 9 countIf(requestStatus = 'success') AS success, 10 10 countIf(requestStatus = 'degraded') AS degraded, ··· 19 19 ORDER BY interval DESC 20 20 21 21 22 + TYPE ENDPOINT
+2 -1
packages/tinybird/pipes/endpoint__tcp_uptime_7d__v1.pipe packages/tinybird/endpoints/endpoint__tcp_uptime_7d__v1.pipe
··· 4 4 SQL > 5 5 6 6 % 7 - SELECT 7 + SELECT 8 8 toStartOfInterval(time, INTERVAL {{ String(interval, '30', required=True) }} minute) AS interval, 9 9 countIf(requestStatus = 'success') AS success, 10 10 countIf(requestStatus = 'degraded') AS degraded, ··· 19 19 ORDER BY interval DESC 20 20 21 21 22 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint__tcp_workspace_30d__v0.pipe packages/tinybird/endpoints/endpoint__tcp_workspace_30d__v0.pipe
··· 13 13 ORDER BY day DESC 14 14 15 15 16 + TYPE ENDPOINT
+1
packages/tinybird/pipes/endpoint_audit_log.pipe packages/tinybird/endpoints/endpoint_audit_log.pipe
··· 6 6 % SELECT * FROM audit_log__v0 WHERE id = {{ String(event_id, 1) }} ORDER BY timestamp DESC 7 7 8 8 9 + TYPE ENDPOINT
+1
packages/tinybird/pipes/external_status.pipe packages/tinybird/endpoints/endpoint_external_status.pipe
··· 10 10 LIMIT {{ Int16(limit, 10000) }} 11 11 12 12 13 + TYPE ENDPOINT
+2 -4
packages/tinybird/pipes/get_result_for_on_demand_check_http.pipe
··· 15 15 workspaceId, 16 16 timing, 17 17 cronTimestamp 18 - FROM mv__http_full_30d 18 + FROM mv__http_full_30d__v0 19 19 where 20 - cronTimestamp = {{ String(timestamp, '1729871694536') }} and 20 + cronTimestamp = {{ String(timestamp, '1729871694536') }} and 21 21 monitorId = {{ String(monitorId, '2260') }} 22 22 and url = {{ String(url , 'https://www.openstatus.dev/api/ping/edge') }} 23 23 order by cronTimestamp desc 24 - 25 -
+1 -3
packages/tinybird/pipes/public_status.pipe
··· 11 11 cronTimestamp, 12 12 count() AS count, 13 13 count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok 14 - FROM ping_response_v5 14 + FROM ping_response__v8 15 15 WHERE 16 16 monitorId = {{ String(monitorId, '1') }} 17 17 {% if defined(url) %} AND url = {{ String(url) }} {% end %} ··· 20 20 GROUP BY cronTimestamp, monitorId 21 21 ORDER BY cronTimestamp DESC 22 22 LIMIT {{ Int16(limit, 5)}} 23 - 24 -
+1 -3
packages/tinybird/pipes/response_details.pipe
··· 5 5 6 6 % 7 7 SELECT * 8 - FROM ping_response__v7 8 + FROM ping_response__v8 9 9 WHERE 10 10 monitorId = {{ String(monitorId, '1') }} 11 11 {% if defined(url) %} AND url = {{ String(url) }} {% end %} 12 12 AND cronTimestamp = {{ Int64(cronTimestamp, 1706467215188) }} 13 13 AND region = {{ String(region, 'ams') }} 14 - 15 -
+1 -3
packages/tinybird/pipes/response_graph.pipe
··· 16 16 round(quantile(0.9)(latency)) as p90Latency, 17 17 round(quantile(0.95)(latency)) as p95Latency, 18 18 round(quantile(0.99)(latency)) as p99Latency 19 - FROM ping_response__v7 19 + FROM ping_response__v8 20 20 WHERE 21 21 monitorId = {{ String(monitorId, '1') }} 22 22 {% if defined(url) %} AND url = {{ String(url) }} {% end %} ··· 24 24 {% if defined(toDate) %} AND timestamp <= {{ Int64(toDate) }} {% end %} 25 25 GROUP BY h, region 26 26 ORDER BY h DESC 27 - 28 -
+1 -3
packages/tinybird/pipes/response_list.pipe
··· 5 5 6 6 % 7 7 SELECT latency, monitorId, region, statusCode, timestamp, url, workspaceId, cronTimestamp, message 8 - FROM ping_response__v7 8 + FROM ping_response__v8 9 9 WHERE monitorId = {{ String(monitorId, 'openstatusPing') }} 10 10 {% if defined(url) %} AND url = {{ String(url) }} {% end %} 11 11 {% if defined(region) %} ··· 22 22 {% end %} 23 23 ORDER BY timestamp DESC 24 24 LIMIT {{Int32(limit, 100)}} 25 - 26 -
-55
packages/tinybird/pipes/response_time_metrics.pipe
··· 1 - VERSION 0 2 - 3 - NODE response_time_metrics__v0_0 4 - SQL > 5 - 6 - % 7 - SELECT 8 - round(avg(latency)) as avgLatency, 9 - round(quantile(0.75)(latency)) as p75Latency, 10 - round(quantile(0.9)(latency)) as p90Latency, 11 - round(quantile(0.95)(latency)) as p95Latency, 12 - round(quantile(0.99)(latency)) as p99Latency, 13 - count() as count, 14 - count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok, 15 - -- TODO: make it more efficient 16 - ( 17 - SELECT max(timestamp) 18 - FROM materialized_view_ping_response_45d_ttl__v7 19 - WHERE 20 - monitorId = {{ String(monitorId, '1') }} 21 - {% if defined(url) %} AND url = {{ String(url) }} {% end %} 22 - AND timestamp 23 - >= toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}} HOUR, 3)) 24 - ) as lastTimestamp, 25 - -- for sorting 26 - toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}} HOUR, 3)) as time 27 - FROM materialized_view_ping_response_45d_ttl__v7 28 - WHERE 29 - monitorId = {{ String(monitorId, '1') }} 30 - {% if defined(url) %} AND url = {{ String(url) }} {% end %} 31 - AND timestamp 32 - >= toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}} HOUR, 3)) 33 - UNION ALL 34 - SELECT 35 - round(avg(latency)) AS avgLatency, 36 - round(quantile(0.75)(latency)) AS p75Latency, 37 - round(quantile(0.9)(latency)) AS p90Latency, 38 - round(quantile(0.95)(latency)) AS p95Latency, 39 - round(quantile(0.99)(latency)) AS p99Latency, 40 - count() as count, 41 - count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok, 42 - NULL as lastTimestamp, -- no need to query the `lastTimestamp` as not relevant 43 - -- for sorting 44 - toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}}*2 HOUR, 3)) as time 45 - FROM materialized_view_ping_response_45d_ttl__v7 46 - WHERE 47 - monitorId = {{ String(monitorId, '1') }} 48 - {% if defined(url) %} AND url = {{ String(url) }} {% end %} 49 - AND timestamp 50 - >= toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}}*2 HOUR, 3)) 51 - AND timestamp 52 - < toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24)}} HOUR, 3)) 53 - 54 - 55 -
-24
packages/tinybird/pipes/response_time_metrics_by_region.pipe
··· 1 - VERSION 0 2 - 3 - NODE response_time_metrics_region__v0_0 4 - SQL > 5 - 6 - % 7 - SELECT 8 - region, 9 - round(avg(latency)) as avgLatency, 10 - round(quantile(0.75)(latency)) as p75Latency, 11 - round(quantile(0.9)(latency)) as p90Latency, 12 - round(quantile(0.95)(latency)) as p95Latency, 13 - round(quantile(0.99)(latency)) as p99Latency 14 - -- max(latency) as maxLatency, 15 - -- min(latency) as minLatency 16 - FROM materialized_view_ping_response_45d_ttl__v7 17 - WHERE 18 - monitorId = {{ String(monitorId, '1') }} 19 - {% if defined(url) %} AND url = {{ String(url) }} {% end %} 20 - AND timestamp 21 - >= toUnixTimestamp64Milli(toDateTime64(now() - INTERVAL {{ Int32(interval, 24) }} HOUR, 3)) 22 - GROUP BY region 23 - 24 -
-49
packages/tinybird/pipes/status_timezone.pipe
··· 1 - VERSION 1 2 - 3 - DESCRIPTION > 4 - TODO: descripe what it is for! 5 - 6 - 7 - NODE group_by_cronTimestamp 8 - SQL > 9 - 10 - % 11 - SELECT 12 - toDateTime(cronTimestamp / 1000, 'UTC') AS day, 13 - toTimezone(day, {{ String(timezone, 'Europe/London') }}) as with_timezone, 14 - toStartOfDay(with_timezone) as start_of_day, 15 - -- latency, 16 - statusCode 17 - FROM ping_response__v7 18 - {% if defined(url) %} AND url = {{ String(url) }} {% end %} 19 - WHERE monitorId = {{ String(monitorId, '1') }} 20 - -- by default, we only query the last 45 days 21 - AND cronTimestamp >= toUnixTimestamp64Milli( 22 - toDateTime64(toStartOfDay(date_sub(DAY, 45, now())), 3) 23 - ) 24 - 25 - 26 - NODE group_by_day 27 - SQL > 28 - 29 - % 30 - SELECT 31 - -- round(avg(latency)) as avgLatency, 32 - -- round(quantile(0.75)(latency)) as p75Latency, 33 - -- round(quantile(0.9)(latency)) as p90Latency, 34 - -- round(quantile(0.95)(latency)) as p95Latency, 35 - -- round(quantile(0.99)(latency)) as p99Latency, 36 - start_of_day as day, 37 - count() AS count, 38 - count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok 39 - FROM group_by_cronTimestamp 40 - GROUP BY start_of_day 41 - ORDER BY start_of_day 42 - WITH FILL 43 - FROM 44 - toStartOfDay(toTimezone(now(), {{ String(timezone, 'Europe/London') }})) 45 - TO toStartOfDay( 46 - toTimezone(date_sub(DAY, 46, now()), {{ String(timezone, 'Europe/London') }}) 47 - ) STEP INTERVAL -1 DAY 48 - 49 -
+7 -2
packages/tinybird/src/client.ts
··· 19 19 if (process.env.NODE_ENV === "development") { 20 20 this.tb = new NoopTinybird(); 21 21 } else { 22 - this.tb = new Client({ token }); 22 + // Use local Tinybird container if available (Docker/self-hosted) 23 + // https://www.tinybird.co/docs/api-reference 24 + const tinybirdUrl = process.env.TINYBIRD_URL || "https://api.tinybird.co"; 25 + this.tb = new Client({ 26 + token, 27 + baseUrl: tinybirdUrl, 28 + }); 23 29 } 24 - // this.tb = new Client({ token }); 25 30 } 26 31 27 32 public get homeStats() {
+4 -1
turbo.json
··· 20 20 "UNKEY_API_ID", 21 21 "UPSTASH_REDIS_REST_URL", 22 22 "UPSTASH_REDIS_REST_TOKEN", 23 - "EXTERNAL_API_URL" 23 + "OPENPANEL_CLIENT_SECRET", 24 + "NEXT_PUBLIC_OPENPANEL_CLIENT_ID", 25 + "AUTH_SECRET", 26 + "CRON_SECRET" 24 27 ], 25 28 "outputs": [".next/**", "!.next/cache/**", "dist/**"] 26 29 },