maistats는 maimaidx-eng.com 데이터를 수집하고, 정적 곡 데이터와 개인 플레이 기록을 각각 분리해서 제공하는 monorepo입니다. 저장소에는 Rust 서비스 3개와 Vite/React 프런트엔드 1개가 들어 있습니다.
핵심 배포 모델은 다음과 같습니다.
maistats-song-info: 개발자가 공용으로 생성/배포maistats-discord-bot: 개발자가 공용으로 호스팅apps/maistats: 개발자가 공용으로 호스팅maistats-record-collector: 각 사용자가 자기 SEGA ID로 직접 호스팅
즉, 곡 정보는 공유되고 플레이 기록은 사용자별 self-hosted collector에서만 관리됩니다.
공용 정적 곡 데이터 생성기입니다.
- 내부
songdb서브시스템으로 곡 목록, 내부 레벨, 재킷 이미지를 준비합니다. - 내부 레벨은 maimai DX NET의 레벨별 곡 목록 페이지를 읽어 매일 다시 추론합니다.
- 실행 시
data/song_data/data.json과data/song_data/cover/를 생성합니다. - GitHub Actions가 매일 07:30 KST에 실행되어 R2로 업로드합니다.
- 공개 경로:
https://maimai-charts.muhwan.dev/data.jsonhttps://maimai-charts.muhwan.dev/cover/<image_name>
개인 플레이 기록 수집 서버입니다.
- 사용자의 SEGA ID로 로그인합니다.
- 프로세스별 임시 cookie store를 사용해 인증 세션을 유지합니다.
- SQLite를 사용하며 런타임에
sqlx::migrate!()로 마이그레이션을 적용합니다. - 시작 시 점수 시드를 보장하고
playerData를 읽은 뒤, 플레이 횟수 변화가 있으면 recent를 동기화합니다. - 이후 10분마다 백그라운드 polling을 수행합니다.
- 유지보수 시간대에는 초기 동기화를 건너뜁니다.
- 대표 엔드포인트:
GET /healthGET /health/readyGET /api/playerGET /api/scores/ratedGET /api/songs/scoresGET /api/recentGET /api/todayGET /api/rating/targets
공용 Discord 봇입니다.
- 전역 Song Database URL을 참조합니다.
- 각 Discord 사용자는
/register <url>로 자기 Record Collector URL을 등록합니다. - 봇 자체 SQLite에
Discord user -> record collector URL매핑을 저장합니다. - 시작 시 slash command를 전역 등록하고, 개발자 계정에만 startup 요약 DM을 보냅니다.
- 주요 커맨드:
/how-to-use/register/mai-score/mai-song-info/mai-recent/mai-today
공용 웹 프런트엔드입니다.
- Vite + React 기반입니다.
- 기본적으로 Song Database와 Record Collector 서버를 각각 다른 origin으로 붙습니다.
- Settings 화면에서 연결 URL을 직접 바꿀 수 있어, 공용 프런트엔드에서 각자 self-hosted collector에 붙는 방식으로 사용할 수 있습니다.
- 주요 화면:
- Scores
- Rating
- Playlogs
- Random Picker
- Settings
.
|-- apps/maistats/ # Vite + React frontend
|-- maistats-song-info/ # shared static song database generator
|-- maistats-record-collector/ # per-user self-hosted record server
|-- maistats-discord-bot/ # shared Discord bot
`-- crates/
|-- maimai-auth/ # maimaidx-eng.com auth helpers
|-- maimai-parsers/ # HTML parsers
`-- models/ # shared API/domain/storage models
- Rust stable
- Node.js 20+
- npm
- SEGA ID 계정
- Discord Bot Token 및 개발자 Discord User ID
- Song Database 생성/업로드까지 사용할 경우 SongDB 관련 인증 정보
루트 .env는 Rust 서비스들이 공용으로 사용합니다.
cp .env.example .env기본 항목:
- Record Collector
SEGA_IDSEGA_PASSWORDRECORD_COLLECTOR_PORTDATA_DIRDATABASE_URL
- Song Database
SONG_DATA_PATH
- Discord Bot
DISCORD_BOT_TOKENDISCORD_DEV_USER_IDSONG_DATABASE_URLDISCORD_BOT_DATABASE_URL
- SongDB updater
MAIMAI_INTL_SEGA_IDMAIMAI_INTL_SEGA_PASSWORDUSER_AGENT
- R2 upload
R2_PUBLIC_BASE_URLR2_CLOUDFLARE_API_TOKENR2_BUCKET_NAMER2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_ENDPOINT
프런트엔드는 별도 .env를 사용합니다.
cp apps/maistats/.env.example apps/maistats/.envSONG_DATABASE_URLRECORD_COLLECTOR_SERVER_URL
프런트엔드의 RECORD_COLLECTOR_SERVER_URL은 기본값일 뿐입니다. 실제 배포에서는 사용자가 Settings에서 자기 collector URL로 덮어쓰는 흐름을 전제로 합니다.
의존성 설치:
npm cicargo run -p maistats-song-info생성 결과: data/song_data/data.json, data/song_data/cover/
cargo run -p maistats-record-collector기본 주소: http://localhost:3000
처음 실행 시 data/를 만들고 DB 마이그레이션을 적용합니다. 인증이나 초기 동기화가 실패해도 서버는 뜨므로, API 상태 확인과 디버깅을 분리해서 진행할 수 있습니다.
cargo run -p maistats-discord-botDiscord 사용자는 /register <record-collector-url>로 자신의 collector를 연결해야 합니다.
npm run dev:maistats기본 주소: http://localhost:5174
권장 운영 형태는 다음과 같습니다.
- 개발자가
maistats-song-info,maistats-discord-bot,apps/maistats를 공용으로 운영합니다. - 각 사용자는 자신의 환경에서
maistats-record-collector를 띄웁니다. - Discord에서는
/register로 collector URL을 등록합니다. - 웹에서는 Settings에서 collector URL을 입력해 같은 공용 프런트엔드에 연결합니다.
이 구조 덕분에 곡 정보와 UI는 한 곳에서 관리하면서도, 플레이 기록 DB와 로그인 세션은 각 사용자 환경에 남길 수 있습니다.
- Record Collector
- SQLite DB: 기본값
data/maimai.sqlite3 - 임시 cookie file: OS temp 디렉터리 아래
maistats-cookies-<pid>.json
- SQLite DB: 기본값
- Song Database
- 곡 JSON: 기본값
data/song_data/data.json - 재킷 이미지: 기본값
data/song_data/cover/
- 곡 JSON: 기본값
- Discord Bot
- 봇 DB: 기본값
data/maistats-discord-bot.sqlite3
- 봇 DB: 기본값
data/와 .env는 커밋하지 않습니다.
cargo fmt --all
cargo clippy --all -- -D warnings
cargo test
npm run build:maistatsmaistats-song-info는 독립 바이너리로 실행해 정적 산출물을 만들고 GitHub Actions가 R2로 업로드합니다.maistats-record-collector와maistats-discord-bot은 독립 바이너리로 배포합니다.- 프런트엔드
apps/maistats는 Cloudflare용 Vite 설정을 사용합니다. - CI/배포 워크플로는
.github/workflows/아래에 있습니다.