メモ: Readest プライベートデプロイメントノート

公開日: 2025-07-28 13:48 更新日: 2025-07-28 18:58 1823文字 10 min read

Readest は、現代のオープンソース電子書籍リーダーであり、没入型読書を目的としたアプリケーションです。GitHub では 10.1k スターを獲得しており、非常に優れたプロジェクトであるとも言えます。しかし、GitHub のプロジェクトページにはプロジェクトの構築に関するガイドしか提供されておらず、詳細な設定方法についての説明は一切ありませんでした。その後、インターネット全体を検索した結果、TM 全網にデプロイメントガイドは存在せず、Readest に関する紹介や宣伝記事ばかりで、内容はほぼ同じで、複数の記事がお互いにコピーしているように見えるほどです。本記事は、私が Readest をプライベート化してデプロイする際のメモであり、現時点ではウェブ端末でのデプロイメント作業のみを完了しており、参考のために記載しました。

AIモデル Qwen/Qwen3-8B による翻訳。

原文言語:Simplified Chinese、翻訳先言語:japanese、翻訳時間:2026-05-01 06:12

AI 翻訳は参考に限り、内容の完全な正確性を保証できません。原文をご参照ください。

Readest は、immersivesな電子書籍リーダーとしての現代的なオープンソースプロジェクトです。

GitHub には 10.1k stars があり、非常に優れたプロジェクトであることは間違いありません。

しかし、GitHub プロジェクトページでは、プロジェクトの構築に関するガイドラインのみが提供されており、構築の設定方法については詳細が省略されています。インターネット全体を調べても、デプロイチュートリアルは見つからず、Readest に関する紹介記事やプロモーション記事がほとんどで、内容はほぼ同じで、10 数記事がお互いにコピーしているように感じました。

本文は私的なデプロイノートであり、現在はWeb 端でのみデプロイを完了しており、参考までにご提供します。

デプロイ環境

Netlify Build

Runtime: Next.js ベースディレクトリ: / パッケージディレクトリ: Not set ビルドコマンド: git submodule update --init --recursive && pnpm i && pnpm --filter @readest/readest-app setup-pdfjs && export NODE_OPTIONS=--max-old-space-size=4096 && pnpm --filter @readest/readest-app build-web 公開ディレクトリ: apps/readest-app/.next

Node.js: 22.x ビルドイメージ: Ubuntu Noble 24.04 (default)

ENV

プロジェクトには、apps/readest-app/.env.local.example という名前のファイルが含まれており、その内容は不完全です。プロジェクト全体で45の環境変数が依存しており、この例ファイルには20 個しか提供されていません。以下の設定に従って補完してください。

必須の環境変数一覧は以下の通りです:

  • NEXT_PUBLIC_API_BASE_URL
  • NEXT_PUBLIC_POSTHOG_KEY
  • NEXT_PUBLIC_POSTHOG_HOST
  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • SUPABASE_ADMIN_KEY
# ====== 核心应用配置 ======
NEXT_PUBLIC_APP_PLATFORM=web # 建议不要填它 应用平台: web/tauri
NEXT_PUBLIC_API_BASE_URL=https://your-api-base-url.com # API基础URL
NEXT_PUBLIC_NODE_BASE_URL= # Node.js服务基础URL(可选)

# ====== 分析监控 ======
NEXT_PUBLIC_POSTHOG_KEY=YOUR_POSTHOG_KEY # PostHog分析密钥
NEXT_PUBLIC_POSTHOG_HOST=YOUR_POSTHOG_HOST # PostHog服务地址
NEXT_PUBLIC_DEFAULT_POSTHOG_URL_BASE64= # PostHog备用URL(base64编码)
NEXT_PUBLIC_DEFAULT_POSTHOG_KEY_BASE64= # PostHog备用密钥(base64编码)

# ====== 数据库 & 认证 ======
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL # Supabase项目URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY # Supabase匿名密钥
SUPABASE_ADMIN_KEY=YOUR_SUPABASE_ADMIN_KEY # Supabase管理员密钥
NEXT_PUBLIC_DEFAULT_SUPABASE_URL_BASE64= # 备用Supabase URL(base64)
NEXT_PUBLIC_DEFAULT_SUPABASE_KEY_BASE64= # 备用Supabase密钥(base64)

# ====== 支付 & 订阅 ======
# Stripe配置
STRIPE_SECRET_KEY= # Stripe生产环境密钥
STRIPE_SECRET_KEY_DEV= # Stripe开发环境密钥
STRIPE_WEBHOOK_SECRET= # Stripe webhook验证密钥
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_BASE64= # Stripe生产公钥(base64)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_DEV_BASE64= # Stripe开发公钥(base64)

# Apple应用内支付
APPLE_IAP_KEY_ID= # Apple IAP密钥ID
APPLE_IAP_ISSUER_ID= # Apple IAP发行者ID
APPLE_IAP_BUNDLE_ID= # Apple应用包ID
APPLE_IAP_PRIVATE_KEY_BASE64= # Apple私钥(base64编码)

# ====== 文件存储 ======
# 存储类型: r2/s3
NEXT_PUBLIC_OBJECT_STORAGE_TYPE=r2 

# Cloudflare R2配置
R2_ACCESS_KEY_ID=YOUR_R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY=YOUR_R2_SECRET_ACCESS_KEY
R2_BUCKET_NAME=YOUR_R2_BUCKET_NAME
R2_ACCOUNT_ID=YOUR_R2_ACCOUNT_ID
R2_REGION=auto # R2存储区域

# AWS S3配置
S3_ENDPOINT= # S3服务端点
S3_ACCESS_KEY_ID= # S3访问密钥
S3_SECRET_ACCESS_KEY= # S3密钥
S3_BUCKET_NAME= # S3存储桶名称
S3_REGION= # S3存储区域

# ====== 资源配额 ======
NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824 # 固定存储配额(字节)
NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA= # 固定翻译配额(可选)

# ====== 翻译服务 ======
DEEPL_PRO_API_KEYS=YOUR_DEEPL_PRO_API_KEYS # DeepL专业版API密钥
DEEPL_FREE_API_KEYS=YOUR_DEEPL_FREE_API_KEYS # DeepL免费版API密钥
DEEPL_X_FINGERPRINT= # DeepL安全指纹
DEEPL_PRO_API= # DeepL专业版API端点
DEEPL_FREE_API= # DeepL免费版API端点

# ====== 高级功能 ======
NEXT_PUBLIC_USE_APPLE_SIGN_IN=false # 启用Apple登录
NEXT_PUBLIC_DISABLE_UPDATER= # 禁用自动更新
NEXT_PUBLIC_DIST_CHANNEL=readest # 发布渠道
ENABLE_IAP_API_TESTS=false # 启用IAP测试
USE_CUSTOM_OAUTH= # 使用自定义OAuth

# ====== 服务器配置 ======
PROTOCOL=http # 服务协议: http/https
HOST=localhost:3000 # 服务主机
ANALYZE=false # 启用bundle分析
R2_TOKEN_VALUE=YOUR_R2_TOKEN_VALUE # R2访问令牌(可选)

Supabase データベース設定

データベーステーブルの作成

以下のSQLを実行してテーブルを作成してください。

公式の Supabase Tables Schema for Sync API にある public.books テーブルは、source_titlemetadata の2つの列が不足しています。transform.ts (apps/readest-app/src/utils/transform.ts) はこれらの列にデータを書き込む試みをしますが、public.books テーブルにこれらの列が存在しないとエラーが発生します。

とにかく、私が提示するこのSQLを実行してください。

create table public.books (
  user_id uuid not null,
  book_hash text not null,
  format text null, -- 'EPUB' | 'PDF' | 'MOBI' | 'CBZ' | 'FB2' | 'FBZ'
  title text null,
  source_title text null,
  author text null,
  "group" text null,
  tags text[] null,
  metadata text null,
  created_at timestamp with time zone null default now(),
  updated_at timestamp with time zone null default now(),
  deleted_at timestamp with time zone null,
  uploaded_at timestamp with time zone null,
  progress integer[] null,
  group_id text null,
  group_name text null,
  constraint books_pkey primary key (user_id, book_hash),
  constraint books_user_id_fkey foreign KEY (user_id) references auth.users (id) on delete CASCADE
) TABLESPACE pg_default;

ALTER TABLE public.books ENABLE ROW LEVEL SECURITY;

CREATE POLICY select_books ON public.books
  FOR SELECT to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY insert_books ON public.books
  FOR INSERT to authenticated WITH CHECK ((select auth.uid()) = user_id);
CREATE POLICY update_books ON public.books
  FOR UPDATE to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY delete_books ON public.books
  FOR DELETE to authenticated USING ((select auth.uid()) = user_id);

create table public.book_configs (
  user_id uuid not null,
  book_hash text not null,
  location text null,
  progress jsonb null,
  search_config jsonb null,
  view_settings jsonb null,
  created_at timestamp with time zone null default now(),
  updated_at timestamp with time zone null default now(),
  deleted_at timestamp with time zone null,
  constraint book_configs_pkey primary key (user_id, book_hash),
  constraint book_configs_user_id_fkey foreign KEY (user_id) references auth.users (id) on delete CASCADE
) TABLESPACE pg_default;

ALTER TABLE public.book_configs ENABLE ROW LEVEL SECURITY;

CREATE POLICY select_book_configs ON public.book_configs
  FOR SELECT to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY insert_book_configs ON public.book_configs
  FOR INSERT to authenticated WITH CHECK ((select auth.uid()) = user_id);
CREATE POLICY update_book_configs ON public.book_configs
  FOR UPDATE to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY delete_book_configs ON public.book_configs
  FOR DELETE to authenticated USING ((select auth.uid()) = user_id);

create table public.book_notes (
  user_id uuid not null,
  book_hash text not null,
  id text not null,
  type text null,
  cfi text null,
  text text null,
  style text null,
  color text null,
  note text null,
  created_at timestamp with time zone null default now(),
  updated_at timestamp with time zone null default now(),
  deleted_at timestamp with time zone null,
  constraint book_notes_pkey primary key (user_id, book_hash, id),
  constraint book_notes_user_id_fkey foreign KEY (user_id) references auth.users (id) on delete CASCADE
) TABLESPACE pg_default;

ALTER TABLE public.book_notes ENABLE ROW LEVEL SECURITY;

CREATE POLICY select_book_notes ON public.book_notes
  FOR SELECT to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY insert_book_notes ON public.book_notes
  FOR INSERT to authenticated WITH CHECK ((select auth.uid()) = user_id);
CREATE POLICY update_book_notes ON public.book_notes
  FOR UPDATE to authenticated USING ((select auth.uid()) = user_id);
CREATE POLICY delete_book_notes ON public.book_notes
  FOR DELETE to authenticated USING ((select auth.uid()) = user_id);

-- Create the `files` table
create table public.files (
  id uuid not null default gen_random_uuid (),
  user_id uuid not null,
  book_hash text null,
  file_key text not null,
  file_size bigint not null,
  created_at timestamp with time zone null default now(),
  deleted_at timestamp with time zone null,
  constraint files_pkey primary key (id),
  constraint files_file_key_key unique (file_key),
  constraint files_user_id_fkey foreign KEY (user_id) references auth.users (id) on delete CASCADE
) TABLESPACE pg_default;

-- Add an index for efficient querying by user_id and deleted_at
create index idx_files_user_id_deleted_at
on public.files (user_id, deleted_at);

create index idx_files_file_key
on public.files (file_key);

create index idx_files_file_key_deleted_at
on public.files (file_key, deleted_at);

-- Enable RLS on the `files` table
alter table public.files enable row level security;

create policy "Users can insert their own files"
on public.files
for insert
with check (
  auth.uid() = user_id
);


create policy "Users can view their own active files"
on public.files
for select
using (
  auth.uid() = user_id and deleted_at is null
);


create policy "Users can soft-delete their own files"
on public.files
for update
using (
  auth.uid() = user_id
)
with check (
  deleted_at is null or deleted_at > now()
);

create policy "Users can delete their own files permanently"
on public.files
for delete
using (
  auth.uid() = user_id
);

SupabaseにおけるサイトURLの設定

Supabase ダッシュボードで認証(左側) > サイトURL を見つけて、サイトURLをウェブサイトURLに変更してください。

ユーザー設定

Readest のユーザーは Supabase で管理されています。認証 > ユーザー でユーザーを追加または削除できます。

認証 > ポリシー でログイン/登録方法(メール/Google/GitHub/Apple)を設定できますが、Readest はログイン画面でこれらのオプションを依然として表示しますが、無効化された方法でログインするとエラーになります。

また、認証 > ポリシー でユーザー登録を無効化することも可能です。

私はすべてスマホで操作しました。スマホの性能が非常に悪いです。 天竜 9300+ 16.0GB RAMで、なぜかTermuxでのデバッグがとても遅く、ウェブページがフォアグラウンドから離れただけで強制終了してしまうのです。スマホの性能が酷いです。

気に入ったならばコメントを残してくださいね~