Sharely — 技術ドキュメント

バージョン: 1.0.0 · ライセンス: MIT · Node.js: ≥ 18

目次

1. プロジェクト概要

2. アーキテクチャ

3. 技術スタック

4. ディレクトリ構造

5. 設定と環境変数

6. データモデル

7. REST API

8. WebSocket API

9. ファイル配信ルート

10. 認証とセキュリティ

11. アップロードシステム

12. サムネイル生成

13. メールとSMTP

14. 国際化

15. フロントエンド(React SPA)

16. 管理ダッシュボード

17. GDPR / プライバシー

18. バックグラウンドジョブ

19. デプロイ

20. 開発環境

21. マイグレーションとスクリプト

22. エンドツーエンドテスト


1. プロジェクト概要

Sharelyは、クリーンなWebインターフェース、ShareX連携、APIアクセスを備えたセルフホスト型ファイル共有プラットフォームです。ユーザーはスクリーンショット、ファイル、メディアをアップロードし、短縮リンクで即座に共有できます。

主要機能

機能説明
Webアップロードドラッグ&ドロップ、最大500ファイルを同時にアップロード可能
チャンクアップロード並列マルチパートアップロードで最大2GBのファイルに対応
ShareX連携ワンクリックで.sxcu設定ファイルをダウンロード
APIアップロードBearerトークン認証、curl/wget互換
ファイルビューア画像ズーム、動画・音声ストリーミング(HTTP Range)、PDFインライン表示、コードのシンタックスハイライト
埋め込みモード*embed*(OG/Twitter Card HTML)または*raw*(直接リダイレクト)
サムネイル動画(ffmpeg)とPDF(ghostscript)の自動JPEGプレビュー生成
コレクションパスワードと有効期限を設定可能なファイルグループ
共有リンクパスワード、有効期限、ダウンロード制限付きのファイル別リンク
リアルタイムUIWebSocketベースのライブ更新(アップロード、削除、閲覧カウンター、管理統計)
多言語対応8言語:EN, DE, FR, ES, IT, PT, JA, ZH
管理ダッシュボード統計、ユーザー管理、ファイル管理、監査ログ(CSVエクスポート)
GDPRコンプライアンスEU GDPRに準拠したプライバシー機能(第17、20、32条など)
XBackBoneインポート既存のXBackBoneインストールの移行
Docker対応docker compose up -dで完全な環境を起動

2. アーキテクチャ

┌─────────────────────────────────────────────────────────────┐
│                        Browser / Client                      │
│            React 18 SPA  ·  Vite  ·  Tailwind CSS           │
│   ┌──────────────────────────────────────────────────────┐   │
│   │  HTTP REST (/api/*)            WebSocket (/ws)       │   │
│   └──────────────────────────────────────────────────────┘   │
└───────────────────────────┬────────────────────┬─────────────┘
                            │ HTTP               │ WS
┌───────────────────────────▼────────────────────▼─────────────┐
│                    Express.js (app.js)                        │
│                                                               │
│  ┌──────────────┐  ┌───────────┐  ┌──────────┐  ┌────────┐  │
│  │  /api/auth   │  │  /api/*   │  │   /f/*   │  │  /s/*  │  │
│  │  /api/install│  │  routes   │  │  files   │  │ shares │  │
│  └──────────────┘  └───────────┘  └──────────┘  └────────┘  │
│                                                               │
│  Middleware: session · rate-limit · CSRF · CSP · auth        │
│                                                               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────────┐  │
│  │  multer  │  │  ws.js   │  │  mailer  │  │ retention  │  │
│  │  upload  │  │ WebSocket│  │ nodemailer│  │  cleanup   │  │
│  └──────────┘  └──────────┘  └──────────┘  └────────────┘  │
└───────────────────────────┬───────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────────┐
│                      MongoDB (Mongoose)                        │
│  User · File · Collection · ShareLink · SiteSettings          │
│  AuditLog                                                      │
└───────────────────────────────────────────────────────────────┘
                            │
              ┌─────────────▼───────────────┐
              │     Dateisystem (uploads/)   │
              │  {folderName}/  ·  .chunks/  │
              │  .thumbnails/   ·  .avatars/ │
              └─────────────────────────────┘

データフロー:標準アップロード

Browser → POST /api/web-upload (multipart/form-data)
       → multer: Datei in uploads/{folderName}/
       → File.createUnique() → MongoDB
       → generateThumbnail() (async, non-blocking)
       → logAudit()
       → broadcast('file:uploaded') via WebSocket
       ← JSON: { files: [...] }

データフロー:ShareXアップロード

ShareX → POST /upload (token im Formular-Body)
       → multer: Datei temporär in uploads/
       → requireApiKey: Token-Lookup → User
       → fs.renameSync → uploads/{folderName}/
       → File.create() → MongoDB
       ← JSON: { url, delete_url }

3. 技術スタック

レイヤー技術バージョン
ランタイムNode.js≥ 18
バックエンドフレームワークExpress.js4.x
データベースMongoDB + MongooseMongo 7, Mongoose 8.x
セッションexpress-session + connect-mongo
リアルタイムWebSocket (ws)8.x
ファイルアップロードMulter1.x
メールNodemailer8.x
パスワードハッシュbcryptjs2.x (12ラウンド)
APIキーハッシュSHA-256 (Node Crypto)
レート制限express-rate-limit8.x
XBackBoneインポートsql.js1.x
フロントエンドフレームワークReact 1818.3.x
ルーティング(フロントエンド)React Router v66.x
ビルドツールVite6.x
スタイリングTailwind CSS + Radix UI3.x
UIコンポーネントshadcn/ui (Radix primitives)
アイコンFontAwesome6.x
i18ni18next + react-i18next
シンタックスハイライトhighlight.js11.x
コンテナDocker + Docker Compose
テストPlaywright (E2E)1.60.x

4. ディレクトリ構造

sharely/
├── app.js                          # Expressエントリーポイント、起動シーケンス
├── package.json
├── .env.example                    # 全環境変数のテンプレート
├── Dockerfile
├── docker-compose.yml
│
├── src/
│   ├── config/
│   │   └── db.js                   # MongoDB接続(Mongoose)
│   ├── middleware/
│   │   ├── auth.js                 # requireLogin / requireAdmin / requireApiKey
│   │   └── upload.js               # Multer設定、ブロックリスト
│   ├── models/
│   │   ├── AuditLog.js             # 監査イベント(TTL 90日)
│   │   ├── Collection.js           # ファイルコレクション
│   │   ├── File.js                 # ファイルメタデータ
│   │   ├── ShareLink.js            # ファイル別共有リンク
│   │   ├── SiteSettings.js         # シングルトン:運営者設定
│   │   └── User.js                 # ユーザーアカウント + APIキー
│   ├── routes/
│   │   ├── api.js                  # メインAPI(アップロード、ギャラリー、管理者など)
│   │   ├── auth.js                 # ログイン / 登録 / パスワードリセット
│   │   ├── files.js                # ファイル配信、OG埋め込み、レンジリクエスト
│   │   ├── import.js               # XBackBone移行
│   │   ├── install.js              # 初期インストールエンドポイント
│   │   └── shares.js               # 共有リンクファイル配信
│   ├── jobs/
│   │   └── retentionCleanup.js     # 期限切れファイルの日次削除
│   ├── migrations/
│   │   ├── migrateApiKeyHashes.js  # 一回限り:平文 → SHA-256ハッシュ
│   │   └── migrateUserFolders.js   # 一回限り:ユーザーフォルダへのファイル移動
│   ├── utils/
│   │   ├── audit.js                # logAudit()ヘルパー関数
│   │   ├── generateThumbnail.js    # ffmpeg / ghostscript統合
│   │   ├── mailer.js               # Nodemailerラッパー + i18nメールテンプレート
│   │   └── sanitizeFilename.js     # ファイル名サニタイゼーション
│   └── ws.js                       # WebSocketサーバー + アクションディスパッチャー
│
├── client/                         # Reactフロントエンド
│   ├── index.html
│   ├── package.json
│   ├── vite.config.js
│   ├── tailwind.config.js
│   └── src/
│       ├── main.jsx                # Reactエントリーポイント
│       ├── App.jsx                 # ルーター設定
│       ├── index.css               # グローバルスタイル
│       ├── context/
│       │   └── AuthContext.jsx     # グローバル認証状態
│       ├── hooks/
│       │   ├── use-toast.js        # トースト通知フック
│       │   └── useWebSocket.js     # WS接続 + イベントハンドラー
│       ├── components/
│       │   ├── Layout.jsx          # アプリシェル(ナビバー、サイドバー)
│       │   ├── ProtectedRoute.jsx  # 認証ガード
│       │   ├── ShareLinkDialog.jsx # 共有リンク作成ダイアログ
│       │   ├── AddToCollectionDialog.jsx
│       │   ├── CookieBanner.jsx
│       │   ├── LanguageSelector.jsx
│       │   ├── RequireEmailDialog.jsx
│       │   ├── UserAvatar.jsx
│       │   └── ui/                 # shadcn/ui基底コンポーネント
│       ├── pages/
│       │   ├── Upload.jsx          # アップロードページ
│       │   ├── Gallery.jsx         # ファイルギャラリー
│       │   ├── FileView.jsx        # ファイル詳細ビュー
│       │   ├── Collections.jsx     # コレクション一覧
│       │   ├── CollectionView.jsx  # 個別コレクション
│       │   ├── ShareView.jsx       # 公開共有リンクページ
│       │   ├── Settings.jsx        # ユーザー設定
│       │   ├── Login.jsx
│       │   ├── Register.jsx
│       │   ├── ForgotPassword.jsx
│       │   ├── ResetPassword.jsx
│       │   ├── Install.jsx         # 初期インストール
│       │   ├── PrivacyPolicy.jsx
│       │   ├── TermsOfService.jsx
│       │   └── admin/
│       │       ├── Dashboard.jsx   # 管理者ホームページ
│       │       ├── Users.jsx       # ユーザー管理
│       │       ├── Files.jsx       # ファイル管理
│       │       ├── AuditLog.jsx    # 監査ログビュー
│       │       ├── SiteSettings.jsx# 運営者設定
│       │       └── Import.jsx      # XBackBoneインポート
│       ├── i18n/
│       │   ├── index.js            # i18next設定
│       │   └── locales/
│       │       ├── de.json
│       │       ├── en.json
│       │       ├── es.json
│       │       ├── fr.json
│       │       ├── it.json
│       │       ├── ja.json
│       │       ├── pt.json
│       │       └── zh.json
│       └── lib/
│           └── utils.js            # Tailwindヘルパー(cn())
│
├── scripts/
│   ├── setup-db.js
│   ├── mongo-init.js               # MongoDB初期化スクリプト
│   ├── migrate-uploads-to-user-folders.js
│   └── generate-missing-thumbnails.js
│
├── e2e/                            # Playwrightテスト
│   ├── admin.spec.js
│   ├── bulk-actions-fixes.spec.js
│   ├── gallery.spec.js
│   ├── sharelink.spec.js
│   ├── tags.spec.js
│   ├── upload.spec.js
│   ├── helpers.js
│   └── global-setup.js
│
├── uploads/                        # ファイルアップロード(ランタイム)
│   ├── .thumbnails/
│   ├── .avatars/
│   └── .chunks/                    # 一時チャンク
│
└── docs/assets/                    # スクリーンショットとロゴ

5. 設定と環境変数

すべての変数は.envから(dotenv経由で)読み込まれます。.env.exampleファイルには完全なテンプレートが含まれています。

必須フィールド

変数説明
SESSION_SECRETセッション暗号化のシークレット — 長いランダム文字列、例:openssl rand -hex 32
MONGO_ROOT_PASSWORDMongoDBルートパスワード(Docker Composeのみ必要)
MONGO_APP_PASSWORDMongoDBアプリケーションユーザーパスワード

全環境変数

変数デフォルト説明
PORT3000HTTPサーバーのTCPポート
MONGODB_URI_(Docker Composeから構築)_完全なMongoDB接続URI
MONGO_ROOT_PASSWORDMongoDBルートパスワード
MONGO_APP_USERappuserMongoDBアプリケーションユーザー名
MONGO_APP_PASSWORDMongoDBアプリケーションユーザーパスワード
MONGO_DB_NAMEsharelyMongoDBデータベース名
SESSION_SECRET必須 — セッション暗号化シークレット
BASE_URLhttp://localhost:3000生成される共有リンクの公開ベースURL(末尾の/なし)
SITE_NAMEsharelyOpen Graph埋め込みのサイト名
MAX_FILE_SIZE_MB100標準アップロードの最大ファイルサイズ(MB)(チャンクアップロードは最大2GBまで独立して対応)
ALLOW_REGISTRATIONtruefalseで公開登録を無効化
SMTP_HOSTSMTPサーバーのホスト名;空欄でメール機能を無効化
SMTP_PORT587SMTPポート
SMTP_SECUREfalse暗黙的TLS(ポート465)ならtrue、STARTTLSならfalse
SMTP_USERSMTPユーザー名
SMTP_PASSSMTPパスワード
SMTP_FROM_(SMTP_USER)_送信メールの差出人アドレス
UPLOAD_DIR./uploadsアップロードディレクトリの絶対パス
NODE_ENVproductionでセキュアCookieを有効化

6. データモデル

User(src/models/User.js

{
  username:                    String (3–32、ユニーク、英数字 + _-)
  password:                    String (bcrypt、12ラウンド)
  role:                        'admin' | 'user'
  apiKey:                      String (レガシー、移行後は空)
  apiKeyHash:                  String (SHA-256、ユニーク、sparse)
  apiKeyPrefix:                String (平文の最初の8文字)
  folderName:                  String (ユニーク、sparse、最大64)
  avatarExt:                   String | null (.jpg/.png/.gif/.webp)
  embedMode:                   'embed' | 'raw'
  isActive:                    Boolean
  email:                       String (小文字、ユニーク、sparse)
  emailVerified:               Boolean
  emailVerificationToken:      String | null (平文のSHA-256ハッシュ)
  emailVerificationExpires:    Date | null
  passwordResetToken:          String | null (SHA-256ハッシュ)
  passwordResetExpires:        Date | null
  language:                    'en'|'de'|'fr'|'es'|'it'|'pt'|'ja'|'zh'
  predefinedTags:              [String] (最大100タグ × 50文字)
  createdAt:                   Date
}

重要なメソッド:

File(src/models/File.js

{
  shortId:      String (16進数8文字:タイムスタンプ6文字 + ランダム2文字、ユニーク)
  deleteToken:  String (16進数32文字、ユニーク)
  originalName: String (サニタイズ済み)
  storedName:   String (相対パス:"folderName/8hex.ext")
  mimeType:     String
  size:         Number (バイト)
  uploader:     ObjectId → User
  views:        Number
  tags:         [String] (最大20 × 50文字)
  createdAt:    Date
}

仮想プロパティ:

Short IDアルゴリズム:

shortId = hex(seconds_since_2024-01-01, 6 chars) + randomBytes(2).hex()

Collection(src/models/Collection.js

{
  shortId:     String (16進数8文字、ユニーク)
  name:        String (最大100)
  description: String (最大500)
  owner:       ObjectId → User
  files:       [ObjectId → File]
  password:    String | null (bcrypt)
  expiresAt:   Date | null
  createdAt:   Date
}
期限切れのコレクションは、有効期限後7日でMongoDB TTLインデックスにより自動削除されます。

ShareLink(src/models/ShareLink.js

{
  token:         String (16進数32文字、ユニーク)
  file:          ObjectId → File
  createdBy:     ObjectId → User
  label:         String (最大100)
  password:      String | null (bcrypt)
  expiresAt:     Date | null
  downloadLimit: Number (-1 = 無制限)
  downloadCount: Number
  createdAt:     Date
}
コレクションと同様:TTLインデックスによる有効期限後7日間の猶予期間。

SiteSettings(src/models/SiteSettings.js

シングルトンドキュメント(_id: 'singleton'):

{
  operatorName:        String
  operatorAddress:     String
  operatorEmail:       String
  cloudflareAnalytics: Boolean
  fileRetentionDays:   Number (0 = 無効)
  encryptionAtRest:    Boolean
  sessionDurationDays: Number (デフォルト:7)
  allowRegistration:   Boolean
}

AuditLog(src/models/AuditLog.js

{
  timestamp: Date (TTLインデックス:90日)
  userId:    ObjectId → User | null
  username:  String | null
  action:    String
  ip:        String | null
  meta:      Mixed (アクション固有のメタデータ)
}

7. REST API

ベースURL:/api

すべてのJSONエンドポイントはContent-Type: application/jsonを返します。エラー:{ "error": "メッセージ" }


認証(/api/auth

メソッドパス認証説明
GET/meセッション現在ログイン中のユーザー
POST/loginログイン(レート制限:10回/15分)
POST/register登録(レート制限あり、最初のユーザーが管理者になる)
POST/logoutログアウト
GET/smtp-enabledSMTPが設定されているか確認
GET/verify-email?token=メールアドレスを認証
GET/verify-reset-token?token=リセットトークンを検証
POST/forgot-passwordパスワードリセットメールを送信(レート制限:5回/時間)
POST/reset-password新しいパスワードを設定

ログインリクエスト:

POST /api/auth/login
{ "username": "max", "password": "meinPasswort123" }

ログインレスポンス:

{
  "user": {
    "id": "...", "username": "max", "role": "user",
    "avatarUrl": null, "email": "[email protected]",
    "emailVerified": true, "language": "de"
  }
}

ファイルアップロード

メソッドパス認証説明
POST/uploadAPIキーShareXアップロード(app.jsのレガシーエンドポイント)
POST/api/uploadAPIキーShareX/APIアップロード(フィールド:file
POST/api/web-uploadセッションWebアップロード(フィールド:files[]、最大500)
POST/api/chunk/initセッションチャンクアップロードの初期化
POST/api/chunk/:uploadIdセッションチャンクのアップロード(フィールド:chunk
POST/api/chunk/:uploadId/completeセッションチャンクの結合
DELETE/api/chunk/:uploadIdセッションアップロードのキャンセルとクリーンアップ

APIアップロードレスポンス:

{
  "url": "https://example.com/f/a1b2c3d4",
  "raw": "https://example.com/f/a1b2c3d4/raw",
  "delete_url": "https://example.com/api/delete/a1b2c3d4",
  "short_id": "a1b2c3d4",
  "filename": "screenshot.png",
  "size": 102400
}

チャンクアップロードフロー

1. POST /api/chunk/init{ uploadId: "32hex" }

2. POST /api/chunk/:uploadId(ボディ:chunkIndex=N、ファイル:chunk)→ { received: N }

3〜5チャンクを並列で送信

3. POST /api/chunk/:uploadId/complete{ files: [fileObject] }


ファイル管理

メソッドパス認証説明
GET/api/galleryセッション自分のファイル(管理者:全ファイル)、ページネーション(24件/ページ)、フィルター:qtypetagpage
GET/api/file/:shortIdファイルメタデータ(閲覧カウンターを増加)
PATCH/api/file/:shortIdセッションタグ/名前の更新
DELETE/api/file/:shortIdセッションファイルの削除
DELETE/api/delete/:shortIdAPIキーファイルの削除(APIキー認証)
POST/api/files/bulkセッション一括操作:deletetagremoveTagaddToCollectionmoveToCollection
GET/api/tagsセッションユーザーの全タグ候補

ギャラリークエリパラメーター:


ユーザー設定

メソッドパス認証説明
GET/api/my-keyセッションAPIキープレフィックスを表示
POST/api/regen-keyセッションAPIキーを再生成
GET/api/sharex-configセッションShareX .sxcuをダウンロード(キーを再生成)
PATCH/api/user/usernameセッションユーザー名を変更(パスワード必須)
PATCH/api/user/passwordセッションパスワードを変更
PATCH/api/user/emailセッションメールを変更(確認メールを送信)
PATCH/api/user/languageセッションUI言語を設定
PATCH/api/user/embed-modeセッション埋め込みモードを設定(embed/raw
POST/api/user/resend-verificationセッション確認メールを再送信
GET/api/user/exportセッションデータエクスポート(GDPR第20条)JSONとして
DELETE/api/user/accountセッションアカウントを削除(GDPR第17条、パスワード必須)
GET/api/user/predefined-tagsセッション定義済みタグを取得
PATCH/api/user/predefined-tagsセッション定義済みタグを更新
POST/api/user/avatarセッションアバターをアップロード(最大2MB、JPEG/PNG/GIF/WebP)
DELETE/api/user/avatarセッションアバターを削除
GET/api/user/avatar/:userIdアバターを配信

共有リンク

メソッドパス認証説明
GET/api/file/:shortId/share-linksセッション(オーナー/管理者)ファイルの全共有リンク
POST/api/file/:shortId/share-linksセッション(オーナー/管理者)共有リンクを作成
DELETE/api/share-links/:tokenセッション(オーナー/作成者/管理者)共有リンクを削除
GET/api/share-links/:token共有リンクのメタデータ(公開)
POST/api/share-links/:token/verify共有リンクのパスワードを確認

共有リンクの作成:

POST /api/file/a1b2c3d4/share-links
{
  "label": "同僚向け",
  "password": "シークレット",
  "expiresAt": "2025-12-31T23:59:59Z",
  "downloadLimit": 10
}

コレクション

メソッドパス認証説明
GET/api/collectionsセッション自分のコレクション(管理者:全コレクション)
POST/api/collectionsセッションコレクションを作成
GET/api/collections/:idコレクションを表示(公開、設定されている場合はパスワード)
PATCH/api/collections/:idセッション(オーナー/管理者)コレクションを更新
DELETE/api/collections/:idセッション(オーナー/管理者)コレクションを削除
POST/api/collections/:id/filesセッション(オーナー/管理者)コレクションにファイルを追加
DELETE/api/collections/:id/files/:fileShortIdセッション(オーナー/管理者)コレクションからファイルを削除
POST/api/collections/:id/verifyコレクションのパスワードを確認

管理者エンドポイント

メソッドパス認証説明
GET/api/admin/stats管理者ダッシュボード統計
GET/api/admin/users管理者全ユーザー(ファイル数を含む)
POST/api/admin/users管理者ユーザーを作成
PATCH/api/admin/users/:id/toggle管理者ユーザーを有効化/無効化
PATCH/api/admin/users/:id/role管理者ユーザーロールを変更
DELETE/api/admin/users/:id管理者ユーザーを削除
POST/api/admin/users/:id/regen-key管理者APIキーを再生成
PATCH/api/admin/users/:id/password管理者パスワードを設定
PATCH/api/admin/users/:id/folder管理者フォルダ名を変更(ファイルを移動)
GET/api/admin/files管理者全ファイル、ページネーション(30件/ページ)
GET/api/admin/site-settings管理者運営者設定を読み取り
PATCH/api/admin/site-settings管理者運営者設定を更新
GET/api/admin/audit-log管理者ページネーション付き監査ログ(50件/ページ)
GET/api/admin/audit-log/export管理者監査ログをCSVでダウンロード
GET/api/site-settings運営者の公開情報(プライバシーページ用)

サイト設定(公開)

GET /api/site-settings
{
  "operatorName": "Musterfirma GmbH",
  "operatorAddress": "Musterstr. 1, 12345 Musterstadt",
  "operatorEmail": "[email protected]",
  "cloudflareAnalytics": false,
  "fileRetentionDays": 365,
  "encryptionAtRest": false,
  "sessionDurationDays": 7
}

8. WebSocket API

接続:wss://example.com/ws(ログイン済みユーザーのみ、セッションCookieが必要)

プロトコル

クライアント → サーバー(リクエスト):

{ "id": "req-abc123", "action": "file:list", "payload": { "type": "image", "page": 1 } }

サーバー → クライアント(レスポンス):

{ "id": "req-abc123", "data": { ... } }

サーバー → クライアント(エラー):

{ "id": "req-abc123", "error": "Forbidden", "status": 403 }

サーバー → クライアント(ブロードキャスト):

{ "event": "file:uploaded", "data": { "shortId": "a1b2c3d4", "uploaderId": "..." } }

利用可能なアクション

アクション認証説明
site-settings:getサイトの公開設定
auth:meユーザーログイン中のユーザーデータ
file:getユーザーファイルの詳細(閲覧数を増加)
file:listユーザーフィルター/ページネーション付きファイルリスト
file:deleteユーザーファイルを削除
user:get-keyユーザーAPIキープレフィックス
user:regen-keyユーザーAPIキーを再生成
user:change-passwordユーザーパスワードを変更
user:change-usernameユーザーユーザー名を変更
user:change-emailユーザーメールを変更
user:change-languageユーザー言語を設定
user:change-embed-modeユーザー埋め込みモードを設定
user:resend-verificationユーザー確認メールを再送信
user:exportユーザーデータエクスポート
user:delete-accountユーザーアカウントを削除
admin:stats管理者ダッシュボード統計
admin:settings:get管理者サイト設定を読み取り
admin:settings:update管理者サイト設定を更新
admin:users:list管理者全ユーザー
admin:users:create管理者ユーザーを作成
admin:users:toggle管理者ユーザーを有効化/無効化
admin:users:role管理者ユーザーロールを変更
admin:users:delete管理者ユーザーを削除
admin:users:regen-key管理者APIキーを再生成
admin:users:password管理者パスワードを設定
admin:users:folder管理者フォルダ名を変更
admin:files:list管理者全ファイル
admin:audit-log:list管理者ページネーション付き監査ログ

ブロードキャストイベント

イベント受信者ペイロード
file:uploadedアップロードしたユーザー{ shortId, uploaderId }
file:deletedファイルオーナー{ shortId, uploaderId }
file:view全員{ shortId, views }
user:created管理者{ id, username, role, ... }
user:deleted管理者{ id }
user:updated管理者{ id, ...変更されたフィールド }
audit:log管理者完全なAuditLogオブジェクト
settings:updated管理者更新されたSiteSettingsオブジェクト
stats:invalidate管理者{}(統計の更新をトリガー)

9. ファイル配信ルート

/f/:shortId — ファイルビューア

- embedMode = 'embed':リダイレクト付きOG HTML

- embedMode = 'raw' + 画像/動画/音声:HTTP 302 → /f/:shortId/raw

/f/:shortId/raw — 直接アクセス

レンジリクエスト対応(206 Partial Content)でファイルをHTTPレスポンスとして配信。画像/動画/音声:Content-Disposition: inline、その他:attachment

/f/:shortId/download — 強制ダウンロード

/rawと同様ですが、常にContent-Disposition: attachment

/f/:shortId/thumb — サムネイル

JPEGサムネイルを返す(動画とPDF用)。Cache-Control: public, max-age=86400

/f/:shortId/delete/:token — ShareX削除

一意の削除トークンを使用して、セッションなしでファイルを削除。

/s/:token — 共有リンクファイル配信(src/routes/shares.js

パスワード(セッションフラグ経由)、有効期限、ダウンロード制限を確認してからファイルを配信。


10. 認証とセキュリティ

セッション認証

APIキー認証

ミドルウェアチェーン(src/middleware/auth.js

requireLogin    → req.session.userを確認 → 未ログインなら401
requireAdmin    → requireLoginと同様、さらにrole === 'admin' → 403
requireApiKey   → Authorizationヘッダーまたはreqbody.tokenを確認

CSRF対策

app.jsrequireSameOrigin()は、すべてのAPIルートでOriginヘッダーをHostヘッダーと比較。sameSite: 'strict' Cookieを補完。

コンテンツセキュリティポリシー

default-src 'self';
script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
media-src 'self' blob:;
connect-src 'self' https://cloudflareinsights.com;
frame-ancestors 'self';

セキュリティヘッダー

レート制限

エンドポイント制限ウィンドウ
アップロード(/upload/api/upload/api/web-upload60リクエスト15分
認証(/api/auth/login/registerなど)10リクエスト15分
パスワードリセット5リクエスト1時間

ファイルブロックリスト

以下のMIMEタイプと拡張子はアップロード時に拒否されます:

ブロックされるMIMEタイプ: application/x-executableapplication/x-shapplication/x-cshapplication/x-bat

ブロックされる拡張子: .bat.cmd.com.ps1.psm1.psd1.sh.bash.csh.zsh.fish.vbs.vbe.jse.scr.pif.application.gadget.hta.php.php3–5.phtml.asp.aspx.jsp.jspx.cfm

パストラバーサル対策

resolveUploadPath()経由のすべてのファイルアクセスは、解決されたパスがUPLOAD_DIR内にあることを確認します。


11. アップロードシステム

標準アップロード(Multer)

チャンクアップロード(>250MB)

フロントエンドクライアントは大きなファイルのために自動的にチャンクモードに切り替えます。

サーバー側のディレクトリ構造:

uploads/.chunks/{uploadId}/
  meta.json          { filename, mimeType, totalSize, totalChunks, userId, createdAt }
  chunk-0
  chunk-1
  ...
  chunk-N

チャンクサイズ: 10〜20MB(後方互換性のため最大51MBを受け入れ)

並列数: 3〜5チャンクの同時アップロード

結合: ストリームベース(RAMへの完全読み込みなし)

アバターアップロード


12. サムネイル生成

サムネイルはアップロード後に非同期で生成されます(.catch(() => {}) — エラーは無視されます)。

動画サムネイル(ffmpeg)

ffmpeg -y -i <file> -ss 00:00:01 -vframes 1 \
  -vf "scale=320:320:force_original_aspect_ratio=increase,crop=320:320" \
  -q:v 3 uploads/.thumbnails/<shortId>.jpg

PDFサムネイル(Ghostscript)

gs -dNOPAUSE -dBATCH -dSAFER \
  -sDEVICE=jpeg -dFirstPage=1 -dLastPage=1 \
  -r72 -dJPEGQ=85 \
  -sOutputFile=uploads/.thumbnails/<shortId>.jpg \
  <file>

タイムアウト: サムネイル生成ごとに30秒

フォールバック: ffmpeg/ghostscriptが利用できない場合、生成はサイレントにスキップされます。

バックフィルスクリプト

npm run migrate:thumbnails
# またはコンテナ内:
docker exec -it <container> npm run migrate:thumbnails

13. メールとSMTP

設定

SMTP_HOSTが設定されている場合にSMTPが有効になります。mailer.isConfigured()でこの値を確認します。

メールテンプレート

すべてのメールはユーザーの言語(8言語)で送信されます。テンプレートはsrc/utils/mailer.jsに組み込まれています。

送信されるメールの種類:

種類トリガートークン有効期限
メール認証登録、メール変更24時間
パスワードリセットPOST /api/auth/forgot-password1時間

トークンセキュリティ


14. 国際化

ライブラリ: i18next + react-i18next + i18next-browser-languagedetector

対応言語:

コード言語
enEnglish
deDeutsch
frFrançais
esEspañol
itItaliano
ptPortuguês
ja日本語
zh中文

言語選択:

1. ブラウザ言語検出(自動)

2. データベースのユーザー設定(user.language

3. PATCH /api/user/languageで永続化

翻訳ファイル:client/src/i18n/locales/{code}.json


15. フロントエンド(React SPA)

ルーター設定(client/src/App.jsx

ルートコンポーネント認証
/リダイレクト → /gallery不要
/auth/loginLogin.jsx不要
/auth/registerRegister.jsx不要
/auth/forgot-passwordForgotPassword.jsx不要
/auth/reset-passwordResetPassword.jsx不要
/installInstall.jsx不要
/uploadUpload.jsx必要
/galleryGallery.jsx必要
/f/:shortIdFileView.jsx必要
/collectionsCollections.jsx必要
/c/:idCollectionView.jsx不要(公開)
/s/:tokenShareView.jsx不要(公開)
/settingsSettings.jsx必要
/adminDashboard.jsx管理者
/admin/usersUsers.jsx管理者
/admin/filesFiles.jsx管理者
/admin/audit-logAuditLog.jsx管理者
/admin/site-settingsSiteSettings.jsx管理者
/admin/importImport.jsx管理者
/privacyPrivacyPolicy.jsx不要
/termsTermsOfService.jsx不要

認証コンテキスト(client/src/context/AuthContext.jsx

ログイン中のユーザーのグローバル状態。アプリ起動時にGET /api/auth/meで初期化。

WebSocketフック(client/src/hooks/useWebSocket.js

永続的なWS接続を管理。sendMessage()とイベントハンドラー登録を提供。接続切断時の再接続ロジック付き。

UIコンポーネント

shadcn/ui(Radix UI Primitives + Tailwind CSS)ベース:


16. 管理ダッシュボード

管理ダッシュボード(/admin/*)はrole: 'admin'のユーザーのみアクセス可能です。

ダッシュボード(/admin

ユーザー管理(/admin/users

ファイル管理(/admin/files

監査ログ(/admin/audit-log

サイト設定(/admin/site-settings

XBackBoneインポート(/admin/import


17. GDPR / プライバシー

機能GDPR条項
プライバシーポリシー(設定可能)第13/14条 – 透明性
利用規約ページ(設定可能)第13/14条 – 透明性
データエクスポート(URLを含むJSON)第20条 – データポータビリティ
アカウント自己削除(ファイル+データ)第17条 – 削除権
監査ログ(MongoDB経由90日TTL)第5条(2) – 説明責任
監査ログCSVエクスポート第5条(2) – 説明責任
設定可能なファイル保持第5条(1)(e) – 保存制限
SHA-256ハッシュとしてのAPIキー第32条 – セキュリティ
bcrypt(12ラウンド)としてのパスワード第32条 – セキュリティ
Cloudflare Analytics用Cookieの同意第13条 – 透明性
アカウント削除時の匿名化第17条 – 削除権

GDPR削除フロー

アカウント削除時(user:delete-account / DELETE /api/user/account):

1. ユーザーの全ファイルがディスクから削除される

2. サムネイルが削除される

3. アバターが削除される

4. 監査ログエントリが匿名化される(username: '[deleted]'ip: nulluserId: null

5. ユーザードキュメントが削除される

6. セッションが破棄される


18. バックグラウンドジョブ

保持クリーンアップ(src/jobs/retentionCleanup.js

MongoDB TTLインデックス(自動)

コレクションTTLトリガー
AuditLog90日timestamp
CollectionexpiresAtの7日後expiresAt
ShareLinkexpiresAtの7日後expiresAt

19. デプロイ

Docker(推奨)

git clone https://github.com/Christianoooooo/sharely.git
cd sharely
cp .env.example .env
# .envを編集(SESSION_SECRET、MONGOパスワード、BASE_URL)
docker compose up -d

サービス:

ボリューム:

ヘルスチェック: アプリは/でHTTP 200を確認、MongoDBはdb.adminCommand('ping')を確認。

Nginxリバースプロキシ

server {
    listen 443 ssl;
    server_name files.example.com;

    client_max_body_size 2100M;  # 最大チャンクサイズ+バッファ以上に設定

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocketサポート
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
.envBASE_URLは公開ドメインと一致する必要があります。

初回起動

最初に登録したユーザーは自動的にadminロールを付与されます(User.countDocuments() === 0)。


20. 開発環境

前提条件

セットアップ

# バックエンド
npm install
cp .env.example .env
# .envを編集

# フロントエンド
cd client
npm install

起動

# バックエンド(ポート3000、nodemonあり)
npm run dev

# フロントエンド(ポート5173、別ターミナル)
cd client
npm run dev

Vite開発サーバーはAPIリクエストを自動的にlocalhost:3000にプロキシします。

ビルド

npm run build   # client/dist/をビルド、バックエンドは変更なし
npm start       # 本番Expressサーバーを起動

21. マイグレーションとスクリプト

自動マイグレーション(毎起動時)

これらのマイグレーションはapp.jsの起動時に実行され、冪等性があります:

マイグレーションファイル機能
ユーザーフォルダマイグレーションsrc/migrations/migrateUserFolders.jsuploads/からuploads/{folderName}/にファイルを移動
APIキーハッシュマイグレーションsrc/migrations/migrateApiKeyHashes.js平文APIキーをSHA-256ハッシュに変換

手動スクリプト

# 既存ファイルのサムネイルを生成
npm run migrate:thumbnails
# または:
node scripts/generate-missing-thumbnails.js

# アップロードをユーザーフォルダに移動(手動)
npm run migrate:user-folders
# または:
node scripts/migrate-uploads-to-user-folders.js

データベース初期化(Docker)

scripts/mongo-init.jsはMongoDBコンテナの初回起動時に実行され、適切な権限でアプリユーザーを作成します。


22. エンドツーエンドテスト

フレームワーク: Playwright(@playwright/test

テストファイル

ファイルテストスイート
e2e/upload.spec.jsアップロードフロー
e2e/gallery.spec.jsギャラリーとファイル管理
e2e/admin.spec.js管理ダッシュボード
e2e/sharelink.spec.js共有リンクの作成と利用
e2e/tags.spec.jsタグ管理
e2e/bulk-actions-fixes.spec.js一括操作

実行

# 全テスト
npm run test:e2e

# UIあり
npm run test:e2e:ui

Playwright設定: playwright.config.js

グローバルセットアップ: e2e/global-setup.js(テストユーザー、管理者などを作成)

ヘルパー: e2e/helpers.js(共有ヘルパー関数)


付録:監査ログアクション

アクショントリガー
loginログイン成功
logoutログアウト
register登録
uploadファイルアップロード
delete_fileファイル削除
delete_accountアカウント削除
change_passwordパスワード変更
change_usernameユーザー名変更
change_emailメール変更
verify_emailメール認証
forgot_passwordパスワードリセットリクエスト
reset_passwordパスワードリセット
regen_api_keyAPIキー再生成
sharex_configShareX設定ダウンロード
export_dataデータエクスポート
admin_create_user管理者:ユーザー作成
admin_delete_user管理者:ユーザー削除
admin_toggle_user管理者:ユーザー有効化/無効化
admin_change_role管理者:ユーザーロール変更
admin_change_password管理者:パスワード設定
admin_regen_key管理者:APIキー再生成

*sharely v1.0.0のソースコードから生成されたドキュメント*