Synaplan
기업을 위한 오픈소스 AI 플랫폼
LUG와 공식 LEGO 팬 커뮤니티를 위한 회원·모임·전시·출석·회비·자원봉사·빌드 챌린지·Discord 관리 도구, 무료 자체 호스팅
Members, meetings, shows, check-ins, dues, volunteers, build challenges and Discord - for LUGs and Recognized LEGO Fan Communities. Free and self-hosted.
비즈니스C++ · HTMLGitHub @ArkLUG온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Run your LEGO User Group (LUG) or LEGO fan community in one place: members, meetings, shows, check-ins, dues, volunteers and Discord. For LUGs and Recognized LEGO Fan Communities (LEGO Fan CoLab). Self-hosted.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Member, meeting, and show management for LUGs and Fan CoLab groups
Run your LEGO® User Group in one place: members, meetings, shows, check-ins, dues, volunteers, build challenges, inventory and money, with Discord (optional) and Google Calendar built in. Self-hosted, open source, and light enough for a small home server.README 전문 보기 ↗Member, meeting, and show management for LUGs and Fan CoLab groups
Run your LEGO® User Group in one place: members, meetings, shows, check-ins, dues, volunteers, build challenges, inventory and money, with Discord (optional) and Google Calendar built in. Self-hosted, open source, and light enough for a small home server.
Who it's for:
Try the demo → It's a fictional "Brickton LUG". Switch between a member, a moderator and an admin from the yellow bar. Nothing you do there is saved.
Notice: This project was built entirely using AI (Claude Code by Anthropic). All code, documentation, templates, and configuration were generated through AI-assisted development.
Members
Meetings and shows
Discord, calendars and the public
Running the LUG
LUG Manager is a single C++ server with SQLite, shipped as a Docker image (ghcr.io/arklug/lug-manager). The one-line version, for any machine with Docker:
mkdir lug-manager && cd lug-manager
curl -O https://raw.githubusercontent.com/ArkLUG/lug-manager/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ArkLUG/lug-manager/main/.env.example
# edit .env: Discord app/bot credentials and LUG_PUBLIC_URL (see Configuration)
docker compose up -d
docker compose logs lug-manager | grep setup
Then:
/setup?token=…. Open it and enter your name and Discord user ID (or an email address).
BOOTSTRAP_ADMIN_DISCORD_ID, and that Discord account becomes admin on its first sign-in.LUG_PUBLIC_URL). Cookies are marked secure when it's https://.Data lives in the /app/data volume: the database, uploads and backups.
Other ways to host it: see HOSTING.md for:
fly.toml)DOCKER.md covers compose, nginx, Kubernetes and backups.
Everything that isn't a secret or needed before the server starts is set in the app under Settings. The public address, the email server and the Discord interactions key can also come from environment variables: then the environment wins, and Settings shows that field as set on the server. Environment variables are read from the environment or from .env; real environment variables always take precedence over .env.
| Variable | Needed | What it's for |
|---|---|---|
DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET | With Discord | Discord sign-in (OAuth2 app) |
DISCORD_REDIRECT_URI | Optional | Defaults to the public address + /auth/callback |
DISCORD_BOT_TOKEN | With Discord | Bot for announcements, roles, DMs and member sync |
LUG_PUBLIC_URL | Optional | The public address, e.g. https://lug.example.org; or set it in Settings > Email & address. Emailed links (sign-in, password reset, email confirmation) are only sent once there is one. |
BOOTSTRAP_ADMIN_DISCORD_ID | Optional | Makes this Discord account admin on first sign-in. The /setup link works too. |
DISCORD_PUBLIC_KEY | Optional | Lets duplicate-member matches be resolved from Discord buttons; or set it in Settings > Discord matches |
LUG_SMTP_URL, LUG_SMTP_USER, LUG_SMTP_PASSWORD, LUG_SMTP_FROM | Optional | The email server. Usually set in Settings > Email & address instead; a variable that is set wins there |
LUG_PORT, LUG_DB_PATH, LUG_TEMPLATES_DIR | Optional | Defaults 8080, ./lug.db, ./src/templates. The Docker image sets these. |
ICAL_TIMEZONE, ICAL_CALENDAR_NAME, DISCORD_GUILD_ID | Optional | Starting values; change them later in Settings |
LUG_OFFLINE | Never in production | 1 blocks every outbound request (Discord, Google, email). Use it for testing and copies of real data. |
LUG_DOTENV | Optional | 0 skips reading .env |
Switch Discord off under Settings > Features (or untick it in the setup checklist). Then:
/setup link with an email address and a password.https://your-host/auth/callback.bot, identify, guilds.DISCORD_PUBLIC_KEY and DISCORD_APPLICATION_ID. Then register https://your-host/discord/interactions as the Interactions Endpoint URL.Members are synced from Discord every 6 hours. A Discord member who may already have a record (for example, a kid added by a parent) is held for review rather than duplicated.
What gets posted is set under Settings > Discord: for each new meeting (announcement, Discord event) and event (thread, announcement, chapter copy, Discord event, update notes), pings, DMs and challenge winners. Each meeting and event can leave parts out for itself. Quiet mode posts nothing new (it only edits and removes), for imports or maintenance. Every post, edit, deletion and failure is in Activity log, where failed posts can be retried. Repair times fixes Discord copies of times sent by older versions without notifying anyone.
Private meetings and events still appear on the calendar, but only as "Private LUG Event", with no title, place or description.
Set the email server and the public address under Settings > Email & address: pick your provider (Gmail, Microsoft 365, Outlook.com, iCloud, Fastmail, Zoho, SendGrid, Mailgun, Amazon SES or other) to fill in the server, then the user name, password and the address to send as. The password is stored encrypted with a key in data/secret.key and never shown again. "Send me a test email" sends one straight away and shows the mail server's error if it fails. The LUG_SMTP_* and LUG_PUBLIC_URL variables still work and win when set. Then:
Every email has a no-login unsubscribe link and one-click List-Unsubscribe headers.
| Role | Can |
|---|---|
| Member | See meetings, events and members (contact details follow each member's privacy choices); RSVP, volunteer, enter challenges, add photos, manage their own account. |
| Moderator (and Chapter Lead) | Everything a member can, plus: add and edit members, manage dues, see all contact details, run meetings and events, review Discord matches. |
| Admin | Everything, including settings, roles, features, reports, deleting members and merging duplicates. |
| Treasurer (flag, any role) | The Treasury page and recording dues there. |
How roles are assigned
Chapter roles (only when Chapters is on)
Privacy: each member chooses, field by field, between Don't share, Verified members (members who have attended in person or paid dues) and All members. Moderators and admins always see everything.
Settings > Features (admin). A switched-off feature disappears from the sidebar, pages and forms, its pages return 404, and its reminders stop. Its data is kept, and the JSON API isn't affected.
Features you can switch off:
These start off and are opt-in:
data/backups; the newest 14 are kept by default. Settings > Backups has "Back up now" and download links.data/backups/uploads with each backup. You can also download them all as one .zip.data/secret.key encrypts the email password stored in the database. It's not in the database backups, so copy it along when moving to a new server (otherwise just re-enter the email password).Full create/read/update/delete under /api/v1/*:
read, write or admin scope. Send the key as X-API-Key: <key> or Authorization: Bearer <key>. Only a hash of each key is stored./static/api-docs.html, with the spec at /static/openapi.yaml.curl -H "X-API-Key: $LUG_API_KEY" https://lug.example.org/api/v1/events
Stack:
# Ubuntu/Debian: sudo apt-get install cmake g++ pkg-config libcurl4-openssl-dev libsqlite3-dev libssl-dev zlib1g-dev
# Arch: sudo pacman -S cmake gcc pkgconf curl sqlite openssl zlib
# macOS: brew install cmake curl sqlite openssl pkg-config
cmake -B build -S . -DBUILD_TESTS=ON
cmake --build build -j$(nproc)
bash scripts/build_css.sh # Tailwind -> src/static/tailwind.min.css
LUG_OFFLINE=1 ./build/lug_manager # http://localhost:8080
ctest --test-dir build --output-on-failure -j$(nproc)
There are 58 test suites, unit and integration. Tests never contact real services:
LUG_OFFLINE=1.tests/support/fake_discord.hpp and tests/support/fake_google.hpp, built on tests/support/fake_server.hpp.Browser checks: these need Firefox and Selenium. Point them at a running server with LUG_BASE.
tests/e2e/smoke.py <session-token> <screenshot-dir> covers every page and pop-up, and fails on any CSP violation or JavaScript error.tests/e2e/mobile.py loads the main pages at phone size and fails if any page scrolls sideways.Safety rules for local runs:
LUG_OFFLINE=1 for development and for any copy of real data..env holds real credentials. Real environment variables take precedence over .env, and LUG_DOTENV=0 skips it.LUG_DISCORD_BASE and LUG_GOOGLE_BASE point the clients at a fake server, and are accepted only for loopback addresses.The workflow .github/workflows/demo.yml builds the demo and publishes it to GitHub Pages:
main, every Monday so the dates stay current, and on demand.scripts/demo/seed.py creates the fictional LUG.scripts/demo/export.py crawls a throwaway offline server once per role and saves its pages and panels as static files.scripts/demo/demo.js maps the app's requests onto those files and blocks anything that would save.tests/e2e/demo_check.py clicks through the published site.To build the demo locally:
bash scripts/demo/build.sh build/lug_manager /tmp/demo-site /lug-manager
mkdir -p /tmp/site && ln -s /tmp/demo-site /tmp/site/lug-manager
python3 -m http.server 8000 --directory /tmp/site # http://localhost:8000/lug-manager/
Discord is one chat::Provider (src/chat/Provider.hpp, implemented in src/integrations/discord/DiscordProvider.hpp). chat::ChatHub decides what to post and keeps it in step; a provider only knows how to talk to its service. To add one:
chat::Provider: post/edit/delete messages, threads (or report caps().threads = false), scheduled events (or false), direct messages, where things go (place(), chapter channels), and mention syntax. inert() must make member-supplied text unable to ping anyone.register_all_routes (svc.chat->add(...)), and add a Features entry and a settings page for its channels.chat_posts, its switches are chat.<id>.<name> settings, and the message templates are shared. Test it like tests/unit/test_chat_hub.cpp does with a recording provider.Code is grouped by area, and every code folder has a CMakeLists.txt that lists its files (add new files there). Everything except main.cpp builds into the lug_core library, which the server and the tests link.
src/
main.cpp start-up, background jobs (sync, reminders, digest, backups)
routes/ HTTP handlers by area: accounts/ members/ events/ meetings/
attendance/ community/ settings/ pages/, and api/
(/api/v1: members/ events/ admin/)
services/ business logic: events/ members/ notifications/, plus
features, audit, backups, photos, Fan CoLab
repositories/ SQLite access: members/ events/ admin/
integrations/ one folder per outside service:
discord/ REST client, OAuth, chat provider; sync/ (members,
roles), matches/ (match queue), time_repair/
google_calendar/ email/ ical/
chat/ chat-service layer: Provider interface, ChatHub, templates
auth/ sessions, passwords, two-factor
middleware/ auth, CSP and security headers, feature gating
db/ models/ config/ async/ utils/ (text/, web/)
templates/ Mustache pages and partials
static/ CSS/JS (vendored), app.js behaviours, OpenAPI docs
sql/migrations/ applied automatically at start-up
tests/
unit/ no server
integration/<area>/ the whole app on a local port, against fake services
support/ test fixture and the fake Discord/Google servers
e2e/ browser checks (Python + Selenium)
scripts/ Tailwind build, theme CSS generator, demo site
cmake/ build helpers (Crow template patch)
unraid/ Unraid Community Applications template
deploy/ one-paste cloud-init setup for VPS providers
fly.toml, render.yaml cloud hosting configs (see HOSTING.md)
The LUG Manager logo:
DISCORD_REDIRECT_URI matches the redirect in the Developer Portal exactly, and that the person is in your Discord server.…@group.calendar.google.com.LUG_TEMPLATES_DIR.admin scope, even to read.GNU Affero General Public License v3.0. You may fork, modify and share LUG Manager, but changes you run as a network service must be shared under the same license.
Pull requests are welcome. Please make sure ctest passes, the code builds without warnings, and new features come with tests.
LEGO® is a trademark of the LEGO Group, which does not sponsor, authorize or endorse this project. LUG Manager is an independent, fan-made tool and is not part of LEGO Fan CoLab.