rudis-cms
rudis-cms は、Markdown/YAMLドキュメントをCloudflare D1(SQLite)、R2(オブジェクトストレージ)、KVにコンパイルするヘッドレスCMSです。
特徴
- スキーマ駆動: YAMLでコンテンツ構造を定義すると、SQLテーブルが自動生成される
- 画像埋め込みMarkdown: Markdown内の画像は自動的に抽出・最適化され、R2にアップロードされる
- 複数のストレージバックエンド: R2(オブジェクト)、KV(キーバリュー)、インラインストレージをサポート
- 差分更新: 変更されたコンテンツのみアップロード、未変更ファイルはスキップ
- ローカル開発: ローカルのSQLiteとファイルストレージでテスト可能なdumpモード
アーキテクチャ
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Markdown/YAML │────▶│ rudis-cms │────▶│ Cloudflare D1 │
│ ドキュメント │ │ │ │ (SQLite) │
└─────────────────┘ │ │ └─────────────────┘
│ │ ┌─────────────────┐
│ │────▶│ Cloudflare R2 │
│ │ │ (オブジェクト) │
└──────────────┘ └─────────────────┘
┌─────────────────┐
────▶│ Cloudflare KV │
│ (キーバリュー) │
└─────────────────┘
ユースケース
- 動的コンテンツを持つ静的サイトジェネレーター
- 構造化メタデータを持つブログプラットフォーム
- 検索機能を持つドキュメントサイト
- Cloudflareのエッジで構造化コンテンツを必要とするあらゆるアプリケーション
インストール
ソースからビルド
rudis-cmsはRustで書かれています。ソースからビルドできます:
git clone https://github.com/namachan10777/rudis-cms
cd rudis-cms
cargo install --path .
要件
- Rust 2024 edition (1.85+)
- Cloudflareアカウント(以下が必要):
- D1データベース
- R2バケット(オプション、オブジェクトストレージ用)
- KV名前空間(オプション、キーバリューストレージ用)
環境変数
Cloudflareへのデプロイには以下の環境変数が必要です:
| 変数 | 説明 |
|---|---|
CF_ACCOUNT_ID | CloudflareアカウントID |
CF_API_TOKEN | D1、R2、KVの権限を持つAPIトークン |
R2_ACCESS_KEY_ID | R2アクセスキーID |
R2_SECRET_ACCESS_KEY | R2シークレットアクセスキー |
Cloudflare APIトークンの作成
- Cloudflareダッシュボード > マイプロフィール > APIトークン へ移動
- 以下の権限でカスタムトークンを作成:
- Account > D1 > Edit
- Account > Workers KV Storage > Edit
- Account > R2 > Edit
R2認証情報の取得
- R2 > R2 APIトークンの管理 へ移動
- 読み書きアクセス権を持つ新しいAPIトークンを作成
クイックスタート
このガイドでは、rudis-cmsでシンプルなブログをセットアップする方法を説明します。
1. 設定ファイルの作成
config.yamlファイルを作成します:
glob: "posts/**/*.md"
name: posts
table: posts
database_id: your-d1-database-id
syntax:
type: markdown
column: body
schema:
id:
type: id
title:
type: string
required: true
date:
type: date
index: true
required: true
body:
type: markdown
required: true
storage:
type: kv
namespace: your-kv-namespace-id
image:
table: post_images
inherit_ids: [post_id]
storage:
type: r2
bucket: your-bucket-name
prefix: images
2. コンテンツの作成
posts/hello-world.mdにMarkdownファイルを作成します:
---
id: hello-world
title: Hello World
date: 2024-01-01
---
これは最初の投稿です!

3. デプロイ
環境変数を設定して実行します:
export CF_ACCOUNT_ID=your-account-id
export CF_API_TOKEN=your-api-token
export R2_ACCESS_KEY_ID=your-r2-key
export R2_SECRET_ACCESS_KEY=your-r2-secret
rudis-cms --config config.yaml batch
4. 進捗表示
rudis-cmsはデプロイ中に進捗を表示します:
📋 Loading configuration...
🔧 Compiling schema...
📄 Processing documents...
⬆️ Uploading to storage...
✅ Completed!
📊 Results:
├── ✅ posts/hello-world.md
│ ├── ⬆️ kv://namespace/hello-world
│ └── ⬆️ r2://bucket/images/image.png
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📄 Entries: 1 total
✅ Successful: 1
⬆️ Uploads: 2
⏱️ Duration: 1.23s
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ローカル開発
Cloudflareを使わずにローカルでテストするには、dumpモードを使用します:
rudis-cms --config config.yaml dump --storage ./storage --db ./db
これにより、ローカルのSQLiteデータベースが作成され、ファイルはディスクに保存されます。
設定
rudis-cmsはYAML設定ファイルを使用してコンテンツ構造を定義します。
基本構造
glob: "posts/**/*.md" # マッチするファイルパターン
name: posts # コレクション名
table: posts # メインテーブル名
database_id: xxx-xxx # Cloudflare D1データベースID
syntax:
type: markdown # または "yaml"
column: body # Markdownコンテンツのカラム(markdown専用)
schema:
# フィールド定義...
トップレベルオプション
| オプション | 必須 | 説明 |
|---|---|---|
glob | はい | コンテンツファイルのglobパターン |
name | はい | コレクション名 |
table | はい | メインデータベーステーブル名 |
database_id | はい | Cloudflare D1データベースID |
preview_database_id | いいえ | プレビュー用の別D1データベース |
syntax | はい | コンテンツフォーマット設定 |
schema | はい | フィールド定義 |
シンタックスオプション
Markdown
syntax:
type: markdown
column: body # Markdownコンテンツのフィールド名
Markdownシンタックスでは、フロントマターフィールドがスキーマフィールドにマッピングされ、本文は指定されたカラムに保存されます。
YAML
syntax:
type: yaml
YAMLシンタックスでは、ファイル全体がYAMLとして解析され、スキーマフィールドにマッピングされます。
プレビューデータベース
プレビュー/下書きコンテンツ用に別のデータベースを指定できます:
database_id: production-db-id
preview_database_id: preview-db-id
--previewフラグを使用すると、本番ではなくプレビューデータベースにデプロイします。
スキーマ定義
スキーマは、コンテンツの構造とデータベースへの保存方法を定義します。
フィールド定義
スキーマ内の各フィールドには名前と設定があります:
schema:
field_name:
type: string
required: true
index: true
共通オプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
type | string | - | フィールド型(必須) |
required | bool | false | フィールドが必須かどうか |
index | bool | false | データベースインデックスを作成 |
ネストされたレコード
records型を使用してネストされたテーブルを定義できます:
schema:
id:
type: id
comments:
type: records
table: post_comments
inherit_ids: [post_id]
schema:
comment_id:
type: id
text:
type: string
これにより、外部キー関係を持つ別のpost_commentsテーブルが作成されます。
inherit_ids
inherit_idsオプションは、子テーブルに含める親IDを指定します:
comments:
type: records
inherit_ids: [post_id] # post_idを外部キーとして含める
深くネストされたレコードの場合、複数のIDを継承できます:
replies:
type: records
inherit_ids: [post_id, comment_id]
例
schema:
id:
type: id
title:
type: string
required: true
published:
type: datetime
index: true
draft:
type: boolean
index: true
body:
type: markdown
storage:
type: kv
namespace: content
tags:
type: records
table: post_tags
inherit_ids: [post_id]
schema:
tag:
type: id
フィールド型
rudis-cmsは様々なデータニーズに対応するフィールド型をサポートしています。
基本型
id
レコードの主キー識別子。
id:
type: id
- SQLiteでは
TEXTとして保存 - 自動的にインデックス化
- テーブル内で一意である必要がある
string
テキストコンテンツ。
title:
type: string
required: true
- SQLiteでは
TEXTとして保存
boolean
真偽値。
published:
type: boolean
index: true
- SQLiteでは
INTEGER(0/1)として保存
date
時刻なしの日付。
published_date:
type: date
index: true
- ISO 8601形式(YYYY-MM-DD)で
TEXTとして保存 date()関数を使用してインデックス化
datetime
時刻付きの日付。
created_at:
type: datetime
index: true
- ISO 8601形式で
TEXTとして保存 datetime()関数を使用してインデックス化
hash
変更検出用のコンテンツハッシュ。
hash:
type: hash
- ファイル内容から自動計算
- キャッシュ無効化に有用
コンテンツ型
markdown
オプションの画像抽出機能付きMarkdownコンテンツ。
body:
type: markdown
required: true
storage:
type: kv
namespace: content-namespace
image:
table: post_images
inherit_ids: [post_id]
embed_svg_threshold: 8192
storage:
type: r2
bucket: my-bucket
prefix: images
config: {}
オプション:
storage: コンパイル済みMarkdownの保存先image: 抽出された画像の設定image.embed_svg_threshold: これより小さい(バイト)SVGファイルはインライン埋め込みconfig: 追加のMarkdown処理オプション
image
単一画像フィールド。
og_image:
type: image
storage:
type: r2
bucket: my-bucket
prefix: og-images
file
汎用ファイル添付。
attachment:
type: file
storage:
type: r2
bucket: my-bucket
prefix: attachments
リレーショナル型
records
複数レコードを持つネストされたテーブル。
tags:
type: records
table: post_tags
inherit_ids: [post_id]
schema:
tag:
type: id
外部キー関係を持つ別テーブルを作成します。
ストレージオプション
rudis-cmsは異なるコンテンツタイプに対応する複数のストレージバックエンドをサポートしています。
R2(オブジェクトストレージ)
画像などのバイナリファイル用のCloudflare R2。
storage:
type: r2
bucket: my-bucket
prefix: images/posts
| オプション | 必須 | 説明 |
|---|---|---|
bucket | はい | R2バケット名 |
prefix | いいえ | オブジェクトのキープレフィックス |
オブジェクトはハッシュに基づくコンテンツアドレスキーで保存され、重複排除が保証されます。
KV(キーバリュー)
コンパイル済みMarkdownなどのテキストコンテンツ用のCloudflare KV。
storage:
type: kv
namespace: content-namespace-id
| オプション | 必須 | 説明 |
|---|---|---|
namespace | はい | KV名前空間ID |
Inline
コンテンツをデータベースに直接保存。
storage:
type: inline
別途ストレージが不要な小さなコンテンツに最適。
Asset
直接配信される静的アセット用。
storage:
type: asset
prefix: static
ストレージポインター形式
データベースでは、ストレージ参照はポインター情報を含むJSONとして保存されます:
{
"hash": "abc123...",
"pointer": "r2://bucket/prefix/key",
"content_type": "image/png",
"size": 12345
}
ポインター形式はストレージタイプを示します:
r2://bucket/key- R2オブジェクトkv://namespace/key- KVエントリasset://path- アセットファイル
重複排除
rudis-cmsはコンテンツハッシュ(BLAKE3)を使用してアップロードを重複排除します:
- アップロード前にファイルハッシュを計算
- 同じハッシュのオブジェクトが存在する場合、アップロードをスキップ
- 進捗表示では新規アップロードは
⬆️、スキップは⏭️
すべてのオブジェクトを強制的に再アップロードするには-f / --forceフラグを使用します。
CLIコマンド
グローバルオプション
rudis-cms --config <CONFIG> <COMMAND>
| オプション | 短縮形 | 説明 |
|---|---|---|
--config | -c | 設定ファイルのパス(必須) |
コマンド
batch
Cloudflareにコンテンツをデプロイ。
rudis-cms -c config.yaml batch [OPTIONS]
| オプション | 短縮形 | 説明 |
|---|---|---|
--force | -f | すべてのオブジェクトを強制的に再アップロード |
--preview | -p | プレビューデータベースにデプロイ |
例:
# 通常のデプロイ
rudis-cms -c config.yaml batch
# すべてを強制的に再アップロード
rudis-cms -c config.yaml batch -f
# プレビューデータベースにデプロイ
rudis-cms -c config.yaml batch --preview
dump
開発/テスト用にローカルファイルにエクスポート。
rudis-cms -c config.yaml dump --storage <PATH> --db <PATH>
| オプション | 説明 |
|---|---|
--storage | ストレージファイルのディレクトリ |
--db | SQLiteデータベースのディレクトリ |
例:
rudis-cms -c config.yaml dump --storage ./local-storage --db ./local-db
show-schema
生成されたスキーマを表示。
rudis-cms -c config.yaml show-schema <SUBCOMMAND>
show-schema sql
SQL DDL文を表示。
rudis-cms -c config.yaml show-schema sql [OPTIONS]
| オプション | 説明 |
|---|---|
--fetch-objects | オブジェクト取得クエリを含める |
show-schema typescript
TypeScript型定義を生成。
rudis-cms -c config.yaml show-schema typescript [OPTIONS]
| オプション | 説明 |
|---|---|
--save <DIR> | ディレクトリに保存 |
--valibot | Valibotスキーマを生成 |
例:
# 標準出力に表示
rudis-cms -c config.yaml show-schema typescript
# Valibotバリデーション付きで保存
rudis-cms -c config.yaml show-schema typescript --save ./generated --valibot
終了コード
| コード | 説明 |
|---|---|
| 0 | 成功 |
| 1 | エラー |
環境変数
必要な環境変数についてはインストールを参照してください。
SQLスキーマ
rudis-cmsはスキーマ定義に基づいてSQLテーブルを自動生成します。
生成されるテーブル
以下のようなスキーマの場合:
table: posts
schema:
id:
type: id
title:
type: string
required: true
date:
type: date
index: true
body:
type: markdown
storage:
type: kv
namespace: content
tags:
type: records
table: post_tags
inherit_ids: [post_id]
schema:
tag:
type: id
以下のSQLが生成されます:
CREATE TABLE IF NOT EXISTS posts (
id TEXT NOT NULL,
title TEXT NOT NULL,
date TEXT NOT NULL,
body TEXT NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX IF NOT EXISTS index_posts_id ON posts(id);
CREATE INDEX IF NOT EXISTS index_posts_date ON posts(date(date));
CREATE TABLE IF NOT EXISTS post_tags (
post_id TEXT NOT NULL,
tag TEXT NOT NULL,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag)
);
CREATE INDEX IF NOT EXISTS index_post_tags_tag ON post_tags(tag);
型マッピング
| rudis-cms型 | SQLite型 | 備考 |
|---|---|---|
id | TEXT NOT NULL | 主キー |
string | TEXT | requiredの場合NOT NULL |
boolean | INTEGER | 0または1 |
date | TEXT | ISO 8601形式 |
datetime | TEXT | ISO 8601形式 |
hash | TEXT | BLAKE3ハッシュ |
markdown | TEXT | ストレージポインター付きJSON |
image | TEXT | ストレージポインター付きJSON |
file | TEXT | ストレージポインター付きJSON |
ストレージポインター
コンテンツフィールド(markdown、image、file)はJSONとして保存されます:
{
"hash": "abc123def456...",
"size": 12345,
"content_type": "text/markdown",
"pointer": "kv://namespace-id/key"
}
外部キー
records型で作成される子テーブルには以下が含まれます:
ON DELETE CASCADE付きの外部キー制約- 親IDを含む複合主キー
生成されたSQLの確認
CLIを使用して生成されたSQLを確認できます:
rudis-cms -c config.yaml show-schema sql