Skip to content
Merged

Dev #115

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Cloud instance (includes marketing pages)
EMBERLY_RUN_CLOUD=true

# Event Worker Configuration
EMBERLY_RUN_EVENT_WORKER=true

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ yarn.lock
bun.lock
bun.lockb
tmp/TODO.md
.claude/

# testing
/coverage
Expand Down
27 changes: 14 additions & 13 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,19 +253,20 @@ Redis is optional for local development but required in production.

Copy `.env.template` → `.env` to get started. Critical variables:

| Variable | Purpose |
| --------------------------------------- | ---------------------------------- |
| `DATABASE_URL` | PostgreSQL connection string |
| `REDIS_URL` | Redis connection string |
| `NEXTAUTH_SECRET` | Session signing key (≥32 chars) |
| `NEXTAUTH_URL` / `NEXT_PUBLIC_BASE_URL` | App base URL |
| `DISCORD_OAUTH_CLIENT_ID/SECRET` | Discord OAuth |
| `GITHUB_OAUTH_CLIENT_ID/SECRET` | GitHub OAuth |
| `VIRUSTOTAL_API_KEY` | Malware scanning for uploads |
| `VULTR_API_KEY` | S3 bucket provisioning |
| `NEXT_PUBLIC_SENTRY_DSN` | Client-side error tracking |
| `EMBERLY_RUN_CLOUD` | Enable cloud/marketing features |
| `EMBERLY_RUN_EVENT_WORKER` | Enable background event processing |
| Variable | Purpose |
| --------------------------------------- | ------------------------------------------------------------------------- |
| `DATABASE_URL` | PostgreSQL connection string |
| `REDIS_URL` | Redis connection string |
| `NEXTAUTH_SECRET` | Session signing key (≥32 chars) |
| `NEXTAUTH_URL` / `NEXT_PUBLIC_BASE_URL` | App base URL |
| `DISCORD_OAUTH_CLIENT_ID/SECRET` | Discord OAuth |
| `GITHUB_OAUTH_CLIENT_ID/SECRET` | GitHub OAuth |
| `VIRUSTOTAL_API_KEY` | Malware scanning for uploads |
| `VULTR_API_KEY` | S3 bucket provisioning |
| `NEXT_PUBLIC_SENTRY_DSN` | Client-side error tracking |
| `EMBERLY_RUN_CLOUD` | Enable cloud features (billing, Nexium, custom domains, marketing pages) |
| `NEXT_PUBLIC_EMBERLY_RUN_CLOUD` | Same value, mirrored for client-side nav — must match `EMBERLY_RUN_CLOUD` |
| `EMBERLY_RUN_EVENT_WORKER` | Enable background event processing |

Never commit `.env`. Never log env vars. `SENTRY_AUTH_TOKEN` is build-time only (CI).

Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.

The format is based on "Keep a Changelog" and follows [Semantic Versioning](https://semver.org/).

## [2.6.0] - 2026-07-27

### Added

- **Self-hosting mode (`EMBERLY_RUN_CLOUD`)** — the flag is now actually enforced instead of being a no-op. When unset (the self-hosted default), billing/Stripe, Nexium (squads/discovery/talent), custom domains, per-user storage buckets, and all Emberly-brand marketing/company content (homepage, pricing, blog, press, changelogs, contact, discord, legal hub, "join the team"/partner/verification/ban-appeal applications) are hidden from navigation and blocked at the route level. Enforcement happens in three layers: `proxy.ts` (`CLOUD_ONLY_PAGE_PATHS` / `CLOUD_ONLY_API_PATHS` in `packages/lib/middleware/constants.ts`), a central redirect in the new `app/(marketing)/layout.tsx`, and per-page defense-in-depth guards on cloud-only dashboard/admin pages. `packages/lib/config/env.ts` adds `isCloudEnabled()` (server) and `isCloudEnabledClient()` (client, via `NEXT_PUBLIC_EMBERLY_RUN_CLOUD`) as the single source of truth.
- **`(marketing)` route group** — all marketing/company pages moved from `app/(main)/` into `app/(marketing)/` (URLs unchanged — route groups don't affect paths). A single layout-level `isCloudEnabled()` check replaces the need for scattered per-page guards on those routes.
- **Admin cloud-only gating** — extended beyond pages into the admin panel itself: the overview quick-nav tiles and pending-applications stat, the user list's Plan/Grant Storage/Grant Custom Domains/Plan-selector fields, the Squad Reports tab in `/admin/reports`, and the Stripe / Cloudflare / GitHub / Vultr / Linode / OVHcloud integration sections in `/admin/settings` are now hidden on self-hosted instances. Discord integration keeps its webhook/bot-token fields (used for general admin alerts) but hides the booster-perks-only Supporter Role ID field.
- **Configurable site name & meta description** — new `general.siteName` / `general.metaDescription` fields in the config schema, editable from a new "Branding" section in `/admin/settings`. `app/layout.tsx`'s `generateMetadata()` and `buildSiteMetadata()` (`packages/lib/embeds/metadata.ts`) now read these instead of hardcoding "Emberly", so self-hosters can rebrand the browser tab title, OG site name, and meta description. Favicon upload already worked and is unchanged.
- **`GET /api/health`** — replaced the placeholder `{status: 'ok'}` stub with real checks: Postgres (`SELECT 1`), Redis (`PING`), a real write+delete round-trip through the active storage provider (cached 30s to avoid churning production storage under frequent polling), BullMQ event-queue depth plus whether a worker is actually consuming it, VirusTotal configuration, and (cloud instances only) Stripe configuration. Reports overall `ok` / `degraded` / `down` (503 only when the database is unreachable) and flags `EMBERLY_RUN_CLOUD` / `NEXT_PUBLIC_EMBERLY_RUN_CLOUD` mismatches as a warning. New `SystemHealthPanel` (`packages/components/admin/system-health.tsx`) surfaces all of this on the `/admin` overview page with auto-refresh.
- **Discord support link always available** — a "Support" link (shared `DISCORD_INVITE_URL` constant in `packages/lib/constants/site.ts`) now appears in the nav's Extras menu, the footer, and both 404/error page variants regardless of cloud mode, so self-hosted users aren't cut off from community support just because the full `/discord` marketing page is gated.

### Fixed

- **BullMQ worker singleton crossing module instances** — `packages/lib/events/init.ts` tracked the running `Worker` in a plain module-level variable, which Next.js instantiates separately per route bundle/hot-reload. `isWorkerRunning()` (used by the new health check) read from a copy of the module where the worker always looked `null`, even though it was genuinely running. Fixed by moving the worker/initialized state onto `globalThis`, the same pattern already used for the Prisma client singleton.
- **`/setup` unreachable on fresh self-hosted instances** — the new self-hosted homepage redirect in `proxy.ts` ran before the app ever checked whether initial setup was needed, so a brand-new instance briefly flashed the login page before the client-side `SetupChecker` caught it. `proxy.ts` now checks `/api/setup/check` first and redirects straight to `/setup` when incomplete.
- **Cloud-only leaks in nav/dashboard surfaces** — `DashboardShell`'s independent tab strip, the dashboard overview quick-actions grid, the nav's external "Status" link, the footer's "Legal Hub" link, and the `/me` profile page's public-profile/Discovery/Applications link row were all still visible/reachable on self-hosted instances despite the underlying pages being gated. All now respect `isCloudEnabled()` / `isCloudEnabledClient()`.

### Changed

- **Nav dropdown layout** — desktop nav dropdown menus (e.g. the "Extras" menu) switched from a 2-column grid to a single-column list; empty sections (all self-hosted, e.g. "Base" when every item is cloud-only) are now filtered out entirely instead of rendering an empty menu.
- **Mobile nav accordion** — expanded sections now get a left-border indent guide and an active background on the header row, matching the dashboard sidebar's existing expandable-section style.

## [2.5.1] - 2026-07-26

### Fixed
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ DISCORD_OAUTH_* # Discord OAuth app credentials
GITHUB_OAUTH_* # GitHub OAuth app credentials
VIRUSTOTAL_API_KEY # File scanning (optional in dev)
NEXT_PUBLIC_SENTRY_DSN # Error tracking (optional in dev)
EMBERLY_RUN_CLOUD # Set true to enable cloud features
EMBERLY_RUN_CLOUD # Set true to enable cloud features (billing, Nexium, custom domains, marketing pages)
NEXT_PUBLIC_EMBERLY_RUN_CLOUD # Same value, mirrored for client-side nav/UI — must match EMBERLY_RUN_CLOUD
EMBERLY_RUN_EVENT_WORKER # Set true to run the event worker
```

Expand Down
69 changes: 29 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,42 @@
# Emberly

Emberly is an open-source platform for modern file storage, sharing, discovery, and identity verification. Build your digital presence with powerful tools for teams and individuals.
Emberly is an open-source, self-hostable platform for modern file storage, sharing, and URL shortening. Run it yourself for a fast, private, self-contained instance, or point it at the hosted Emberly cloud for team collaboration, custom domains, and billing on top of the same codebase.

[![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) [![CodeQL Advanced](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly.svg?type=shield&issueType=security)](https://app.fossa.com/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly?ref=badge_shield&issueType=security)

## Features

Self-hosted instances get the full core platform below. The sections marked **Cloud** are specific to the hosted Emberly service and aren't part of a self-hosted deployment.

**File Storage & Sharing**

- S3-compatible object storage with configurable upload limits
- S3-compatible or local object storage with configurable upload limits
- Secure file sharing with customizable access controls
- File organization, tagging, and search
- OCR-powered text extraction from uploaded images and documents
- URL shortening with redirect tracking
- Bandwidth-efficient delivery through global infrastructure

**Domain & Branding**
- Code snippet pastes with syntax highlighting

- Custom domain support with annual registration
- Personal or team branded file-sharing pages
- Domain SSL certificate management
- DNS configuration assistance

**Identity & Verification**
**Administrative Tools**

- User verification badges with multiple tier options
- Verification queue with application review system
- Badge display on public profiles
- Organization verification for teams
- User management dashboard with role-based permissions
- Content/user report review queue
- Configurable storage provider, upload limits, and registration controls
- Audit logs and system health monitoring
- Custom branding (site name, meta description, favicon, theme)

**Team & Collaboration**
**Cloud — Team & Collaboration**

- Squad-based team subscriptions with seat-based pricing
- Squad-based team workspaces with seat-based pricing
- Granular permission management (roles: `SUPPORT`, `DEVELOPER`, `MODERATOR`, `DESIGNER`, `STAFF`)
- Team member invitations and management
- Shared storage pools with usage tracking
- Talent discovery profiles and opportunity boards (Nexium)

**Applications & Trust**
**Cloud — Domains & Billing**

- Staff application system for organizational partnerships
- Partner program enrollment
- Verification badge applications
- Ban appeal process with review workflow
- Email notifications for all application updates

**Administrative Tools**

- Promo code management with configurable discounts
- User management dashboard
- Application review queue with multi-stage triage
- Custom domain support with annual registration
- Stripe-backed subscriptions, promo codes, and storage add-ons
- Verification badges, staff/partner applications, and ban-appeal workflows
- Service status page ([emberlystat.us](https://emberlystat.us))
- Analytics and usage reporting

## Quick Start

Expand Down Expand Up @@ -81,14 +67,16 @@ cp .env.template .env
bun run db:generate
bun run db:migrate

# (Optional) Seed subscription plans
# (Optional, cloud only) Seed subscription plans
bun run db:seed

# Start development server
bun dev
```

The application will be available at `http://localhost:3000`.
The application will be available at `http://localhost:3000`. The first time you visit it, you'll be walked through a setup wizard to create the initial admin account and configure storage.

This gives you a fully self-hosted instance out of the box — uploads, pastes, short URLs, and admin tools all work with no extra configuration. The cloud-only features listed above (team workspaces, custom domains, billing) are specific to the hosted Emberly service and aren't part of a standard self-hosted deployment.

In development, the event worker runs in-process automatically — no extra steps needed. See [Event Worker](#event-worker) for production deployment.

Expand All @@ -107,15 +95,15 @@ In development, the event worker runs in-process automatically — no extra step
- [PostgreSQL](https://www.postgresql.org/) — relational database
- [Prisma ORM](https://www.prisma.io/) — database toolkit and migrations
- [Redis](https://redis.io/) + [BullMQ](https://docs.bullmq.io/) — caching, rate limiting, and background job queue
- [Stripe](https://stripe.com/) — payment processing
- [Resend](https://resend.com/) — transactional email delivery
- [Stripe](https://stripe.com/) — payment processing (cloud only, not required to self-host)
- [Resend](https://resend.com/) or SMTP — transactional email delivery

**Infrastructure & Services**

- S3-compatible object storage (AWS S3 / Vultr) — file storage
- Local disk or S3-compatible object storage (AWS S3 / Vultr / Linode / OVHcloud) — file storage
- [NextAuth](https://next-auth.js.org/) — authentication (Discord OAuth, GitHub OAuth, credentials)
- [Sentry](https://sentry.io/) — error tracking and monitoring
- [VirusTotal](https://www.virustotal.com/) — file scanning on upload
- [Sentry](https://sentry.io/) — error tracking and monitoring (optional)
- [VirusTotal](https://www.virustotal.com/) — file scanning on upload (optional)

**Development Tools**

Expand All @@ -127,7 +115,8 @@ In development, the event worker runs in-process automatically — no extra step

```
app/ Next.js App Router pages and routes
(main)/ Public user pages (auth, admin, user profiles)
(main)/ Core app pages (auth, dashboard, admin, user profiles)
(marketing)/ Marketing/company pages (cloud only — hidden when self-hosted)
(raw)/ Raw file serving
(shorturl)/ Short URL redirects
api/ ~180 REST API endpoints
Expand Down
Loading
Loading