Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

109 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”Ί Lambda CMS

A modern, self-hosted CMS + CRM built for developers. Block editor Β· CRM Β· Email campaigns Β· In-browser image editor Β· 2FA Β· Headless API Β· No cloud lock-in.

License: MIT Laravel PHP Vue

πŸ“– Read the Docs


πŸ“Έ Screenshots

Dashboard Posts
Dashboard β€” stats, charts, and recent activity Posts β€” list with search, filters, and bulk actions
CRM Contacts CRM Companies
CRM Contacts β€” manage people, companies, and activities CRM Companies β€” organize contacts under companies
CRM Deals CRM Call Lists
Deals β€” pipeline board with Lead β†’ Won stages Call Lists β€” organize contacts for outreach
Email Templates Subscribers
Email Templates β€” customize system email content Subscribers β€” manage newsletter signups
Campaigns
Campaigns β€” create and send email newsletters

πŸ’‘ What is Lambda CMS?

Lambda CMS is a clean, fast, fully self-hosted content management system with a built-in CRM and email marketing toolkit. No subscriptions, no SaaS lock-in β€” you own your data and your stack. Built with Laravel 12, Vue 3, and Inertia.js, it feels like a modern SPA while keeping the server-side simplicity of a traditional Laravel app.

πŸ› οΈ Tech Stack

Layer Technology
Backend Laravel 12, PHP 8.2+
Frontend Vue 3 (Composition API), Inertia.js 2, Tailwind CSS 4, Vite 7
UI shadcn-vue, reka-ui, Lucide icons
Rich Text Tiptap
Database SQLite (default) or MySQL

✨ Features

πŸ“ Content Management

  • Posts β€” full CRUD with Tiptap rich text or the drag-and-drop block editor
  • Pages β€” static pages built entirely with the block editor
  • Categories & Tags β€” many-to-many on posts; categories support custom colors
  • Scheduled publishing β€” set a future date and posts go live automatically via the scheduler
  • Autosave β€” drafts save quietly in the background while you write
  • Revisions β€” full history with one-click restore (up to 25 per post/page)
  • Draft previews β€” shareable token-based URLs, no login required
  • Bulk actions β€” publish, draft, or delete multiple posts at once

🧱 Block Editor

30+ block types across six categories:

Category Blocks
Content Paragraph, Heading, Quote, Code, Divider, Spacer, HTML, Accordion, Tabs, Embed
Media Image, Gallery, Video
Layout Section, Container (flex / grid / inline-flex)
Interactive Button, CTA, Link, Navigation, Search, Filter Link, Active Filter, Icon List
Data Loop, Pagination, Post Card, Post Title, Post Body, Post Featured Image, Post Meta, Post Author, Post Taxonomy, Post Comments, Archive Title
Site Nav Header, Site Footer, Masthead, Band, Template

What makes it nice to use:

  • Drag-and-drop canvas with cross-list nesting and a layers panel tree
  • Per-block Style tab β€” typography, colors, background (solid / gradient / image), border, shadow, spacing
  • Dynamic field bindings β€” link block content to loop or post-context data sources
  • Per-block Advanced settings β€” custom CSS classes, inline CSS, font family
  • Conditional block visibility based on loop field values
  • Block labels shown in canvas and layers panel

πŸ–ΌοΈ Media Library & Image Editor

  • Upload images, documents, video, and audio (configurable size limit, default 10 MB)
  • In-browser image editor β€” crop with 9 aspect ratio presets, rotate, flip, 8 filter presets (Normal, Vivid, Muted, B&W, Warm, Cool, Fade, Drama), and manual brightness / contrast / saturation / blur controls. Opens before upload and on any existing library item.
  • Auto-resize images larger than the configured limit (default 1920 px wide)
  • Alt text and description per file; UUID-based filenames with dimension tracking
  • Admins see all files; users see their own

🎨 Template System

  • 7 pre-shipped system templates β€” Post Card, Default Blog Index, Default Single Post, Default Archive, Default Search Results, Default Header, Default Footer
  • Custom templates share the same block editor, autosave, and revisions
  • Loop blocks correctly inherit context for dynamic bindings

🌐 Public Frontend

  • Every public page rendered from editable block templates
  • Header and footer rendered from dedicated block templates
  • Blog index, single post, category/tag archives, and full-text search
  • RSS feed at /feed and XML sitemap at /sitemap.xml
  • Design token system β€” accent color applies live to both admin and blog
  • Admin bar visible to authenticated users only

πŸ’¬ Comments

  • Public submission with honeypot spam protection and rate limiting
  • Admin moderation β€” approve, reject, or delete individually or in bulk
  • Nested replies with email notifications
  • Per-post toggle to enable/disable comments

πŸ‘₯ CRM

A built-in customer relationship management system with fine-grained, permission-based access control. Each CRM module has its own permission that can be granted independently to non-admin users.

  • Contacts β€” manage people with name, email, phone, position, notes, and company association. Statuses: active, inactive, archived. Full-text search across all fields. Activity timeline on the edit page.
  • Companies β€” organize contacts under companies with domain, phone, and address. Contacts and deals counts shown in the list view.
  • Deals β€” track sales opportunities through a 5-stage pipeline: Lead, Qualified, Proposal, Won, Lost. Each deal tracks monetary value, expected close date, and links to a contact and company. Closing a deal as Won or Lost auto-stamps closed_at; reopening clears it. Activity timeline per deal.
  • Call Lists β€” create lists of contacts for outreach campaigns. Add/remove contacts, track call status per contact (not called, called, no answer, callback, completed), record notes per call, and work through the list with a dedicated calling interface.
  • Activities β€” log notes, calls, emails, meetings, and tasks against any contact, company, or deal. Shown as a timeline on the entity's edit page.
  • CRM Import β€” upload a CSV, preview rows, map columns to contact or company fields, choose a conflict strategy (skip or overwrite), and import in bulk.
  • CRM Export β€” download contacts, companies, or call lists as CSV files.

Permissions: manage contacts, manage companies, manage deals, manage call lists β€” each grantable independently to any user role.

πŸ“§ Email System

Customizable transactional emails, subscriber management, and email campaigns β€” all in one place.

  • Editable system email templates β€” 5 built-in templates (password reset, email verification, welcome, new comment, comment reply) stored in the database with a WYSIWYG editor. Each template supports merge tags (e.g. {{user_name}}, {{reset_url}}), live preview, and one-click reset to default. All Laravel system emails (password reset, verification, welcome, comment notifications) route through these templates automatically.
  • Subscriber management β€” a standalone subscribers table (separate from CRM contacts) for newsletter signups. Public subscribe form (Blade page at /subscribe) and token-based unsubscribe link. Admin interface with search, status filtering (active / unsubscribed), bulk delete, CSV export, and CSV import with column mapping and conflict strategy.
  • Email campaigns β€” create newsletters with the Tiptap editor, save as draft, and send to all active subscribers. Sending is handled by a queued job that processes each recipient individually. Per-recipient tracking with sent/failed/pending status and error messages. Campaign report page with stat cards and a recipients table.
  • Campaign scheduling β€” schedule campaigns for a future date and time instead of sending immediately. The Laravel scheduler checks every minute and auto-dispatches due campaigns. Scheduled campaigns can be edited (resets to draft), unscheduled back to draft, or sent immediately.
  • Unsubscribe footer β€” every campaign email includes an unsubscribe link with the subscriber's unique token.

Permission: manage email β€” covers templates, subscribers, and campaigns.

πŸ‘€ Users & Roles

  • Two roles: administrator and user
  • Admins manage all content, users, settings, pages, and templates
  • Users manage their own posts, categories, tags, and media
  • Fine-grained permissions: manage contacts, manage companies, manage deals, manage call lists, manage email β€” each independently assignable
  • Avatar upload per profile
  • User invite flow β€” auto-generated password + welcome email
  • User banning with reason and optional expiry (auto-lifted on next login)
  • Online status tracking

πŸ” Auth & Security

  • Email + password login with rate limiting (5 attempts / 60 s per email + IP)
  • Two-factor authentication β€” TOTP-based 2FA compatible with Google Authenticator, Authy, 1Password, and any RFC 6238 app. Includes 8 single-use recovery codes.
  • Forgot / reset password via email token
  • Email verification required before dashboard access
  • Author-scoped post and media access

πŸ”— Webhooks

  • Events: post.published, post.updated, post.deleted, page.published, page.updated, page.deleted
  • HMAC-SHA256 request signature (X-Lambda-Signature) when a secret is set
  • Dispatched via queued jobs; last_triggered_at tracked per webhook

πŸ“… Editorial Calendar

  • Month-view calendar with published, scheduled, and draft posts
  • Unscheduled drafts panel alongside the calendar
  • Admins see all; users see their own

βš™οΈ Settings

  • Site name, URL, timezone, and date format
  • SEO defaults (meta description, OG image, keywords, title separator)
  • SMTP mail configuration with test-send
  • Media upload limits and image resize threshold
  • Comments (enabled toggle, items per page)
  • Appearance: accent color applied live to admin and blog frontend
  • UI density: default, compact, or comfortable
  • Custom JS injection on every public page

πŸ”Œ REST API (Headless)

GET  /api/v1/posts
GET  /api/v1/posts/{slug}
GET  /api/v1/categories
GET  /api/v1/categories/{slug}
GET  /api/v1/tags
GET  /api/v1/tags/{slug}
POST /api/v1/query        <- block editor loop data source

πŸ“¦ Import / Export

Export your content as a portable ZIP and re-import it into any Lambda CMS instance.

Supported entities: Posts, Pages, Categories, Tags, Media, Templates

Export β€” pick which entities to include, optionally bundle media files, and download a .zip. Import β€” upload a ZIP, preview what's inside, select entities, choose a conflict strategy (skip, overwrite, or duplicate), and run.

πŸ“‹ ZIP structure and JSON schemas

The export ZIP contains a manifest.json, one JSON file per entity type, and an optional media/ folder:

lambda-cms-export-2026-06-15-120000.zip
β”œβ”€β”€ manifest.json
β”œβ”€β”€ posts.json
β”œβ”€β”€ pages.json
β”œβ”€β”€ categories.json
β”œβ”€β”€ tags.json
β”œβ”€β”€ media.json
β”œβ”€β”€ templates.json
└── media/
    β”œβ”€β”€ a1b2c3d4-photo.jpg
    └── e5f6g7h8-document.pdf

manifest.json

{
  "version": "1.0",
  "app": "lambda-cms",
  "exported_at": "2026-06-15T12:00:00.000000Z",
  "entities": ["posts", "pages", "categories", "tags", "media", "templates"],
  "include_media_files": true,
  "counts": {
    "posts": 12,
    "pages": 3,
    "categories": 5,
    "tags": 8,
    "media": 20,
    "templates": 4
  }
}

posts.json

[
  {
    "title": "My First Post",
    "slug": "my-first-post",
    "excerpt": "A short summary",
    "body": "<p>HTML content</p>",
    "body_format": "html",
    "status": "published",
    "featured": false,
    "published_at": "2026-06-01T10:00:00.000000Z",
    "comments_enabled": true,
    "use_block_editor": false,
    "blocks": [],
    "meta_title": null,
    "meta_description": null,
    "meta_keywords": null,
    "custom_js": null,
    "categories": ["tutorials", "laravel"],
    "tags": ["php", "cms"],
    "featured_image": "a1b2c3d4-photo.jpg"
  }
]
Field Type Notes
title string Required
slug string Used for conflict detection. Auto-generated on duplicate strategy
excerpt string|null Plain text summary
body string|null HTML content (when body_format is html)
body_format "html" | "blocks" Determines whether body or blocks holds the content
status "draft" | "published" | "scheduled" Defaults to draft on import
featured boolean
published_at ISO 8601|null
comments_enabled boolean
use_block_editor boolean
blocks array Block editor JSON (when body_format is blocks)
meta_title string|null SEO title override
meta_description string|null SEO description
meta_keywords string|null SEO keywords
custom_js string|null Per-post JavaScript
categories string[] Array of category slugs β€” resolved on import
tags string[] Array of tag slugs β€” resolved on import
featured_image string|null Media filename β€” resolved to a media ID on import

pages.json

[
  {
    "title": "About Us",
    "slug": "about-us",
    "status": "published",
    "blocks": [
      { "type": "heading", "data": { "text": "About Us", "level": 1 } },
      { "type": "paragraph", "data": { "text": "Welcome to our site." } }
    ],
    "meta_title": null,
    "meta_description": null,
    "meta_keywords": null,
    "custom_js": null
  }
]
Field Type Notes
title string Required
slug string Used for conflict detection
status "draft" | "published" Defaults to draft on import
blocks array Block editor JSON
meta_title string|null SEO title override
meta_description string|null SEO description
meta_keywords string|null SEO keywords
custom_js string|null Per-page JavaScript

categories.json

[
  {
    "name": "Tutorials",
    "slug": "tutorials",
    "description": "Step-by-step guides",
    "color": "#5e81ac",
    "hue": 213
  }
]

tags.json

[
  {
    "name": "PHP",
    "slug": "php"
  }
]

media.json

[
  {
    "id": 1,
    "filename": "a1b2c3d4-photo.jpg",
    "original_filename": "photo.jpg",
    "disk": "public",
    "path": "media/a1b2c3d4-photo.jpg",
    "mime_type": "image/jpeg",
    "type": "image",
    "size": 204800,
    "width": 1920,
    "height": 1080,
    "alt": "A landscape photo",
    "description": null
  }
]

When include_media_files is enabled, the actual files are bundled under media/ in the ZIP, keyed by filename.

templates.json

[
  {
    "title": "Default Blog Index",
    "type": "index",
    "loop_source": "posts",
    "status": "published",
    "blocks": [],
    "meta_title": null,
    "meta_description": null,
    "meta_keywords": null
  }
]
Field Type Notes
title string Used with type for conflict detection
type "index" | "single" | "archive" | "search" | "header" | "footer" | "partial" Template type
loop_source string|null Data source for loop blocks (e.g. "posts")
status "draft" | "published" When importing a published non-partial template, other published templates of the same type are demoted to draft
blocks array Block editor JSON

Conflict strategies

Strategy Behavior
Skip Leave existing records untouched. Only create records that don't exist yet.
Overwrite Update existing records with the imported data. Matched by slug (posts, pages, categories, tags), filename (media), or title + type (templates).
Duplicate Always create new records, generating a unique slug when needed.

πŸ§™ Installer

  • 4-step browser wizard: Database > Site > Admin > Mail
  • Supports SQLite and MySQL; tests the DB connection before writing .env
  • Runs migrations, seeds system templates, and creates the first admin account

πŸš€ Getting Started

Requirements

  • PHP 8.2+
  • Composer
  • Node.js 20+ and npm
  • SQLite (bundled with PHP) or MySQL 8+

Install

git clone https://github.com/mariusberget92/lambda-cms.git
cd lambda-cms

composer install
npm install

cp .env.example .env
php artisan key:generate

npm run build

Open the site in a browser β€” you'll be redirected to /install to run the setup wizard.

Local Development

php artisan serve
npm run dev

Production

php artisan config:cache
php artisan route:cache
php artisan view:cache
npm run build

Add a cron entry to run the scheduler (handles auto-publishing scheduled posts and dispatching scheduled campaigns):

* * * * * cd /path/to/lambda-cms && php artisan schedule:run >> /dev/null 2>&1

For email campaigns, webhook dispatches, and other queued jobs, start a queue worker:

php artisan queue:work

πŸ§ͺ Testing

php artisan test

The test suite covers 677 tests across content management, CRM, email system, campaign scheduling, and public-facing features.


πŸ—ΊοΈ Roadmap

  • Full import / export (posts, pages, categories, tags, media, templates)
  • CRM (contacts, companies, deals, call lists, activities, import/export)
  • Email system (editable templates, subscribers, campaigns with scheduling)
  • Comprehensive test suite (677 tests)
  • API write access β€” token-based auth for creating and updating content
  • Multi-language / i18n content support

πŸ“„ License

MIT β€” do whatever you want with it.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages