Eidolon-SC
그들은 자신의 목소리로 일본어를 말하고 자막을 제공한다; 전화를 걸면 이전 대화를 기억한다. 비공식 팬 작품, 캐릭터는 AI가 연기
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A modern interest-based community backend written in Go with DDD (Domain-Driven Design), similar to Baidu Tieba / Reddit.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗English | 简体中文
A modern interest-based community backend written in Go with DDD (Domain-Driven Design), similar to Baidu Tieba / Reddit.
Qubar is a full-featured interest community platform offering:
| Component | Version | Purpose |
|---|---|---|
| PostgreSQL | 18 | Primary database, UUIDv7 primary keys, JSONB support |
| Redis | 7+ | Cache & session store, atomic ops via Lua scripts |
| Elasticsearch | 8.x | Full-text search with real-time index sync (via CDC) |
| Redpanda | latest | Kafka-compatible message queue for async stats aggregation & notification fan-out |
| AWS S3 | - | Object storage (images/videos) with presigned URLs |
| Nacos | 3.x | Configuration center with multi-environment support |
| Mailtrap | - | Email delivery (verification codes / notifications) |
The project follows a modular monolith architecture, with packages split by domain boundaries so it can be smoothly decomposed into microservices later:
pkg/
├── composition/ # Composition layer: wire dependencies, register routes, cross-domain Facade bridges
│ ├── hertzadapter/ # Hertz framework adapter → framework-agnostic routing abstraction
│ └── middleware/ # Global middleware (CORS, logging)
│
├── domains/ # Domain layer (each domain is self-contained)
│ ├── auth/ # Authentication (login, registration, OAuth)
│ ├── user/ # User (profiles, search)
│ ├── category/ # Category (circle categories)
│ ├── circle/ # Circle (creation, membership, permissions, management)
│ ├── post/ # Post (publishing, lists, details)
│ ├── comment/ # Comment (two-level flattened structure)
│ ├── like/ # Like (atomic ops + events)
│ ├── collect/ # Favorites
│ ├── history/ # Browsing history
│ ├── discover/ # Discover page aggregation
│ ├── trending/ # Trending posts
│ ├── recommend/ # Home feed recommendation
│ ├── notice/ # Message center (notifications + SSE unread stream)
│ ├── aiagent/ # AI agents (global + circle-scoped bots, reply triggers)
│ ├── storage/ # Storage (file uploads)
│ └── [domain]/
│ ├── application/ # Application services: use-case orchestration
│ ├── domain/ # Domain layer: models, repository interfaces, core business rules
│ ├── infrastructure/ # Infrastructure: repository impls, cache, search, events
│ └── interfaces/http/ # Interface layer: handlers, routes, DTOs
│
├── shared/ # Shared kernel (domain-agnostic)
│ ├── appctx/ # Context abstraction
│ ├── domain/ # Domain base classes (BaseModel)
│ ├── httputil/ # HTTP response utilities
│ └── routing/ # Framework-agnostic routing abstraction
│
├── conf/ # Configuration loading (Nacos + local fallback)
└── logger/ # Logging initialization
routing.RouterGroup abstractionroot_id marks hierarchy, avoiding recursive queries and enabling efficient paginationQubar ships with a two-tier AI agent system:
/agent/*)users.role=1) only/circle/agent/*)api_protocol / base_url / api_key) and delete: owner onlyusers.agent_circle_id synced to ES through CDC)| Trigger | Description |
|---|---|
Keyword (trigger_mode=2) | New comments containing configured keywords trigger a bot reply (async, silent-fail) |
| @Mention | Mentioning an enabled bot when publishing a post triggers it directly, regardless of mode |
Manual (trigger_mode=3) | POST /agent/:id/reply/:postId (admin) or POST /circle/agent/:id/reply/:postId (circle owner), synchronous |
api_key is encrypted at rest and never echoed back – responses only contain has_api_key + api_key_maskedrole=2) used as its commenting identityDesign docs: docs/agent-reply-design.md, docs/circle-agent-manage-design.md, API reference: docs/circle-agent-manage-api.md
qubar/
├── cmd/
│ ├── main.go # Program entry point
│ └── apps/
│ └── server.go # Service initialization & resource orchestration
│
├── configs/
│ ├── config.example.yaml # Config template; copy to config.yaml (git-ignored, fallback when Nacos is unavailable)
│ └── bootstrap.yaml # Nacos bootstrap config (address, namespace, group)
│
├── migrations/ # Database migrations (golang-migrate, source of truth for the schema)
│
├── docs/
│ ├── pgsql-ddl/ # Column semantics, split by domain (schema lives in migrations/)
│ ├── db.md # DDL entry point (points to migrations/ and pgsql-ddl/)
│ ├── api/ # API docs
│ └── design/ # Design docs
│
├── pkg/
│ ├── composition/ # Composition layer (see architecture)
│ ├── domains/ # Business domains (see architecture)
│ ├── shared/ # Shared kernel
│ ├── conf/ # Configuration management
│ ├── logger/ # Logging setup
│ └── server/ # Legacy infrastructure (being migrated)
│ ├── auth/ # OAuth provider implementations
│ ├── storage/ # DB/Redis/ES/Redpanda/S3 initialization
│ └── utils/ # Utility functions
│
├── go.mod
└── go.sum
Only Go and Docker are required:
make dev-up # start PG18 / Redis / ES(IK) / Redpanda (deploy/compose/dev; ports PG 25432 / Redis 26379 / ES 29200 / Redpanda 29092) and run all migrations
make dev-run # run the app with configs/config.dev.yaml, http://localhost:8888/healthz
make verify # before committing: fmt + vet + lint + unit tests + migration checks and replay (same as CI)
make test-integration # integration tests against the dev stack (PG / Redis / Redpanda); required when touching infrastructure/ or SQL
The dev stack has no CDC (connect / Debezium), so business indices in ES are not synced; S3 / OAuth / email use placeholder config and those endpoints won't work (override with QUBAR_* env vars when needed). Integration tests are //go:build integration files next to the code they cover, with shared fixtures in pkg/shared/testkit (isolated random IDs, Redis DB 15, per-run Redpanda topics); they skip when the stack is unreachable. The ports avoid 15432, which is the local ssh -L port to the production database; never point dev commands at it. The sections below cover running against locally installed services.
uuidv7() function enabled)git clone https://github.com/l0sgAi/qubar.git
cd qubar
go mod download
The schema is managed by migrations/ (golang-migrate, forward-only); see migrations/README.md for the rules. docs/pgsql-ddl/ only documents column semantics (and seed data). Running migrations locally requires brew install golang-migrate (Squawk for make migrate-check is downloaded into bin/ automatically).
Fresh local database: run .github/ci/pg-init.sql as a superuser (creates the qubar_owner / qubar_web_app / debezium roles, the domains schema and default privileges; all passwords are ci, local use only), then apply all migrations as qubar_owner:
createdb qubar
psql -d qubar -f .github/ci/pg-init.sql
make migrate-up # add PGPASSWORD=ci if pg_hba is not trust
Existing local database (tables created by hand from docs/pgsql-ddl/): mark the baseline instead of running it; tables must be owned by qubar_owner:
make migrate-force v=20261015000000
Schema changes: make migrate-new name=xxx → edit the SQL → make migrate-up → make migrate-check && make migrate-test (replays all migrations on a throwaway PG18 container, same as CI; needs only Docker).
Copy the template and fill in your database, Redis and other connection info. configs/config.yaml is git-ignored — never commit real secrets:
cp configs/config.example.yaml configs/config.yaml
openssl rand -base64 32 # value for security.data_key (encrypts AI agent API keys; back it up, it can't be rotated)
The template defaults are production-safe (log.level: info, pgsql.log_mode: error, CORS limited to the real domain). For local development, uncomment the localhost CORS origins and lower log levels as needed. Unsafe settings are logged as [config] warnings at startup.
server:
port: 8888
pgsql:
path: 127.0.0.1
port: 5432
db_name: qubar
username: your_username
password: your_password
redis:
host: 127.0.0.1
port: 6379
db: 0
Create configs/bootstrap.yaml:
nacos:
endpoint: "your-nacos-address:8848"
namespace: "dev-namespace-uuid"
group: "QUBAR_GROUP"
data_id: "qubar-dev-conf"
username: "nacos"
password: "nacos"
# Start with local config
go run cmd/main.go -c configs/config.yaml -b ""
# Start with Nacos config
go run cmd/main.go -c configs/config.yaml -b configs/bootstrap.yaml
The service starts at http://localhost:8888
Every config key can be overridden with QUBAR_ + the key path in upper case, . → _. Env wins over Nacos / YAML, so secrets can be injected by the platform instead of written to files:
QUBAR_PGSQL_PASSWORD=... QUBAR_SECURITY_DATA_KEY=... QUBAR_REDPANDA_BROKERS=a:9092,b:9092 \
go run cmd/main.go -c configs/config.yaml -b ""
docker build -t qubar . # multi-arch: docker buildx build --platform linux/amd64,linux/arm64 -t qubar .
docker run -p 8888:8888 -v "$PWD/configs/config.yaml:/etc/qubar/config.yaml:ro" \
-e QUBAR_PGSQL_PASSWORD=... qubar
The image is distroless (nonroot, no shell) and contains no config. It has a built-in HEALTHCHECK (/qubar -probe <url>).
| Path | Meaning |
|---|---|
GET /healthz | Liveness: 200 while the process is up; no dependency checks |
GET /readyz | Readiness: 200 only if PostgreSQL and Redis respond, else 503. Elasticsearch is reported but optional |
| Method | Path | Description |
|---|---|---|
GET | /auth/google/login | Google OAuth login redirect |
GET / POST | /auth/google/callback | Google OAuth callback / one-time code exchange |
GET | /auth/github/login | GitHub OAuth login |
GET / POST | /auth/github/callback | GitHub OAuth callback / code exchange |
GET | /auth/azure/login | Azure AD OAuth login |
GET / POST | /auth/azure/callback | Azure AD OAuth callback / code exchange |
POST | /auth/register/send-code | Send registration verification code |
POST | /auth/register/verify | Verify the code |
POST | /auth/register/complete | Complete registration |
POST | /auth/login | Email/password login |
POST | /auth/password/send-code | Send password-reset code |
POST | /auth/password/verify | Verify password-reset code |
POST | /auth/password/reset | Reset password |
| Method | Path | Description |
|---|---|---|
GET | /user/get | Get current logged-in user |
PUT | /user/update | Update profile |
GET | /user/search | Search users |
GET | /user/detail/:id | Get user detail |
POST | /auth/logout | Revoke current token |
| Method | Path | Description |
|---|---|---|
GET | /category/get | Get all categories |
| Method | Path | Description |
|---|---|---|
POST | /circle/create | Create a circle (login) |
GET | /circle/list | Search/browse circles (public) |
GET | /circle/active | Recently active circles (public) |
GET | /circle/random | Random circles (public) |
GET | /circle/detail/:id | Circle detail (public) |
GET | /circle/user | Circles a user joined (public) |
GET | /circle/posts | Posts in a circle (public) |
GET | /circle/my | Circles I joined (login) |
POST | /circle/join | Join a circle (login) |
POST | /circle/leave | Leave a circle (login) |
GET | /circle/members | Member list (admin+) |
GET | /circle/manage/list | Circles I can manage (owner/admin) |
POST | /circle/manage/role | Assign/revoke admin (owner) |
POST | /circle/manage/transfer | Transfer ownership (owner) |
POST | /circle/manage/mute / unmute | Mute / unmute member (admin+) |
POST | /circle/manage/ban / unban | Ban / unban member (admin+) |
POST | /circle/manage/remove | Remove a member without banning (admin+) |
PUT | /circle/update | Edit circle profile (owner/admin, field-level) |
| Method | Path | Description |
|---|---|---|
POST | /post/create | Publish a post (login, supports @mentions) |
GET | /post/list | Search posts (public) |
GET | /post/home | Home recommendation feed (public) |
GET | /post/my | My posts (login) |
GET | /post/user/:user_id | Posts by a user (public) |
GET | /post/detail/:id | Post detail (public) |
| Method | Path | Description |
|---|---|---|
POST | /comment/create | Post a comment/reply (login) |
GET | /comment/list | Top-level comments (public) |
GET | /comment/replies | Replies within a thread (public) |
GET | /comment/detail/:id | Single comment detail (public) |
| Method | Path | Description |
|---|---|---|
POST | /like/toggle | Like/unlike (posts & comments) |
POST | /collect/toggle | Favorite/unfavorite a post |
GET | /collect/posts | My favorite posts |
| Method | Path | Description |
|---|---|---|
GET | /notice/list | Notification list |
GET | /notice/unread-count | Unread count |
POST | /notice/read | Mark notifications as read |
POST | /notice/read-all | Mark all as read |
GET | /notice/stream | SSE stream for real-time unread count push |
| Method | Path | Description |
|---|---|---|
GET | /discover/ | Discover page aggregation (login) |
GET | /trending/ | Trending posts (public) |
GET | /history/posts | Browsing history (login) |
| Method | Path | Description |
|---|---|---|
POST | /agent | Create a global agent |
GET | /agent/list | List global agents (offset pagination) |
GET | /agent/:id | Agent detail |
PUT | /agent/:id | Update agent (partial) |
DELETE | /agent/:id | Soft-delete agent |
POST | /agent/:id/reply/:postId | Manually trigger a reply |
| Method | Path | Description | Permission |
|---|---|---|---|
POST | /circle/agent | Create a circle agent (≤5 per circle) | admin+ |
GET | /circle/agent/list | List circle agents | admin+ |
GET | /circle/agent/:id | Agent detail | admin+ |
PUT | /circle/agent/:id | Update agent | operational fields: admin+; credential fields: owner only |
DELETE | /circle/agent/:id | Soft-delete agent | owner only |
POST | /circle/agent/:id/reply/:postId | Manually trigger a reply (post must be in the agent's circle) | owner only |
| Method | Path | Description |
|---|---|---|
POST | /upload/image | Single image upload |
POST | /upload/post-images | Multi-image upload for posts |
POST | /upload/video | Video upload |
DELETE | /upload/delete | Delete a file |
GET | /upload/presign | Get a presigned upload URL |
Include this header for all endpoints requiring login:
satoken: your-token-here
pkg/domains/ following the application/domain/infrastructure/interfaces layeringpkg/composition/composition/<domain>_bridges.go (filed under the domain that receives it); per-domain wiring goes in composition/wire_<domain>.gopkg/server/auth/ (see google.go)provider.goauth domain routesKey configuration items:
# CORS allowed origins
cors:
allowed_origins:
- "https://qubar.site"
- "http://localhost:*"
# Sa-Token session config
sa_token:
token_name: "satoken"
timeout: 259200 # 3 days (seconds)
active_timeout: 1800 # 30-minute activity check
is_concurrent: true # Allow concurrent logins
# File upload size limit (50MB configured in code)
# server.WithMaxRequestBodySize(50 << 20)
# Run unit tests
go test ./pkg/...
# Run tests for a specific package
go test ./pkg/composition/middleware/...
Issues and Pull Requests are welcome!
See CONTRIBUTING.md for local setup, conventions, testing and the PR workflow.
This repo is developed with agents (l0sgAi/qubar#67); the rules live in CLAUDE.md:
agent:triage. Once a maintainer relabels one agent:ready, Claude in GitHub Actions picks it up and opens a PR (at most 3 at a time).develop and follow the PR template; CI must pass (go / db / image / iac / gitleaks / integration), and every PR gets an automatic Claude review.risk:high / hold merge automatically once checks pass. PRs touching migrations, deployment, CI, auth or config get risk:high and need a maintainer comment /approve <head-sha>.develop → main deploys to production and is done by maintainers only.For questions or suggestions, please open an Issue or contact the maintainer.
Note: Before the first run, make sure all required parameters are configured correctly, especially database connections and OAuth credentials.