@c-time/frelio-cli 1.3.12 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,10 +38,14 @@ export function generateConfigJson(config) {
38
38
  siteTitle: config.siteTitle,
39
39
  productionUrl: config.productionUrl,
40
40
  previewUrl: config.previewUrl,
41
+ pagesProjectName: config.pagesProjectName,
42
+ adminPagesProjectName: config.adminPagesProjectName,
43
+ r2BucketName: config.r2BucketName,
44
+ r2PublicUrl: config.r2PublicUrl,
41
45
  }, null, 2);
42
46
  }
43
47
  export function generateWranglerToml(config) {
44
- return `name = "${config.pagesProjectName}"
48
+ return `name = "${config.adminPagesProjectName}"
45
49
  compatibility_date = "2024-01-01"
46
50
 
47
51
  [[r2_buckets]]
@@ -52,34 +56,34 @@ bucket_name = "${config.r2BucketName}"
52
56
  R2_PUBLIC_URL = "${config.r2PublicUrl}"
53
57
  `;
54
58
  }
55
- export function generateUsersIndex(config) {
59
+ export function generateUsersJson(config) {
56
60
  const owner = config.ownerUsername;
57
- return JSON.stringify({
58
- users: [
59
- {
60
- githubUsername: owner,
61
- displayName: owner,
62
- isOwner: true,
63
- permissions: {
64
- canViewUsers: true,
65
- canEditUsers: true,
66
- canViewStaging: true,
67
- canEditStaging: true,
68
- canViewContentType: true,
69
- canEditContentType: true,
70
- canViewBuildRecipes: true,
71
- canEditBuildRecipes: true,
72
- canViewTemplates: true,
73
- canEditTemplates: true,
74
- canViewStorage: true,
75
- canEditStorage: true,
76
- canViewDeploy: true,
77
- canEditDeploy: true,
78
- },
61
+ return JSON.stringify([
62
+ {
63
+ githubUsername: owner,
64
+ displayName: owner,
65
+ isOwner: true,
66
+ permissions: {
67
+ canViewUsers: true,
68
+ canEditUsers: true,
69
+ canViewStaging: true,
70
+ canEditStaging: true,
71
+ canViewContentType: true,
72
+ canEditContentType: true,
73
+ canViewBuildRecipes: true,
74
+ canEditBuildRecipes: true,
75
+ canViewTemplates: true,
76
+ canEditTemplates: true,
77
+ canViewStorage: true,
78
+ canEditStorage: true,
79
+ canViewDeploy: true,
80
+ canEditDeploy: true,
79
81
  },
80
- ],
81
- stagingBranches: [],
82
- }, null, 2);
82
+ },
83
+ ], null, 2);
84
+ }
85
+ export function generateStagesJson() {
86
+ return JSON.stringify([], null, 2);
83
87
  }
84
88
  export function generateContentTypesJson() {
85
89
  return JSON.stringify([], null, 2);
@@ -324,6 +328,326 @@ export function generateTsConfigNode() {
324
328
  include: ['scripts/**/*.ts'],
325
329
  }, null, 2);
326
330
  }
331
+ // --- Terraform generators ---
332
+ export function generateTerraformProviders() {
333
+ return `terraform {
334
+ required_version = ">= 1.5.0"
335
+
336
+ required_providers {
337
+ cloudflare = {
338
+ source = "cloudflare/cloudflare"
339
+ version = "~> 5.0"
340
+ }
341
+ }
342
+
343
+ # Cloudflare R2 をリモートバックエンドとして使う場合はコメントを外す:
344
+ # backend "s3" {
345
+ # bucket = "terraform-state"
346
+ # key = "frelio/terraform.tfstate"
347
+ # region = "auto"
348
+ # skip_credentials_validation = true
349
+ # skip_metadata_api_check = true
350
+ # skip_region_validation = true
351
+ # skip_requesting_account_id = true
352
+ # skip_s3_checksum = true
353
+ # endpoints = {
354
+ # s3 = "https://<account_id>.r2.cloudflarestorage.com"
355
+ # }
356
+ # }
357
+ }
358
+
359
+ provider "cloudflare" {
360
+ api_token = var.cloudflare_api_token
361
+ }
362
+ `;
363
+ }
364
+ export function generateTerraformVariables(config) {
365
+ return `# --- 必須変数 ---
366
+
367
+ variable "cloudflare_account_id" {
368
+ type = string
369
+ description = "Cloudflare Account ID"
370
+ }
371
+
372
+ variable "cloudflare_api_token" {
373
+ type = string
374
+ sensitive = true
375
+ description = "Cloudflare API Token (Pages + R2 の操作権限が必要)"
376
+ }
377
+
378
+ variable "github_client_id" {
379
+ type = string
380
+ description = "GitHub OAuth App Client ID"
381
+ }
382
+
383
+ variable "github_client_secret" {
384
+ type = string
385
+ sensitive = true
386
+ description = "GitHub OAuth App Client Secret"
387
+ }
388
+
389
+ # --- リソース名 ---
390
+
391
+ variable "r2_bucket_name" {
392
+ type = string
393
+ default = "${config.r2BucketName}"
394
+ description = "R2 バケット名"
395
+ }
396
+
397
+ variable "pages_project_name" {
398
+ type = string
399
+ default = "${config.pagesProjectName}"
400
+ description = "コンテンツ配信 Pages プロジェクト名"
401
+ }
402
+
403
+ variable "admin_pages_project_name" {
404
+ type = string
405
+ default = "${config.adminPagesProjectName}"
406
+ description = "管理画面 Pages プロジェクト名"
407
+ }
408
+
409
+ variable "r2_public_url" {
410
+ type = string
411
+ default = "${config.r2PublicUrl}"
412
+ description = "R2 公開 URL(例: https://yourdomain.com/storage)"
413
+ }
414
+
415
+ # --- オプション: カスタムドメイン ---
416
+
417
+ variable "production_domain" {
418
+ type = string
419
+ default = ""
420
+ description = "本番カスタムドメイン(空なら *.pages.dev で運用)"
421
+ }
422
+
423
+ variable "admin_domain" {
424
+ type = string
425
+ default = ""
426
+ description = "管理画面カスタムドメイン(空なら *.pages.dev で運用)"
427
+ }
428
+
429
+ variable "staging_domain" {
430
+ type = string
431
+ default = ""
432
+ description = "ステージングカスタムドメイン(空ならブランチプレビュー URL で運用)"
433
+ }
434
+ `;
435
+ }
436
+ export function generateTerraformMain(config) {
437
+ return `# --- R2 バケット ---
438
+
439
+ resource "cloudflare_r2_bucket" "files" {
440
+ account_id = var.cloudflare_account_id
441
+ name = var.r2_bucket_name
442
+ }
443
+
444
+ # --- Pages プロジェクト(コンテンツ配信) ---
445
+
446
+ resource "cloudflare_pages_project" "content" {
447
+ account_id = var.cloudflare_account_id
448
+ name = var.pages_project_name
449
+ production_branch = "main"
450
+ }
451
+
452
+ # --- Pages プロジェクト(管理画面) ---
453
+
454
+ resource "cloudflare_pages_project" "admin" {
455
+ account_id = var.cloudflare_account_id
456
+ name = var.admin_pages_project_name
457
+ production_branch = "admin"
458
+
459
+ deployment_configs {
460
+ production {
461
+ secrets = {
462
+ GITHUB_CLIENT_SECRET = var.github_client_secret
463
+ GITHUB_CLIENT_ID = var.github_client_id
464
+ }
465
+ environment_variables = {
466
+ R2_PUBLIC_URL = var.r2_public_url
467
+ }
468
+ }
469
+ }
470
+ }
471
+
472
+ # --- カスタムドメイン(オプション) ---
473
+
474
+ resource "cloudflare_pages_domain" "production" {
475
+ count = var.production_domain != "" ? 1 : 0
476
+
477
+ account_id = var.cloudflare_account_id
478
+ project_name = cloudflare_pages_project.content.name
479
+ domain = var.production_domain
480
+ }
481
+
482
+ resource "cloudflare_pages_domain" "admin" {
483
+ count = var.admin_domain != "" ? 1 : 0
484
+
485
+ account_id = var.cloudflare_account_id
486
+ project_name = cloudflare_pages_project.admin.name
487
+ domain = var.admin_domain
488
+ }
489
+
490
+ resource "cloudflare_pages_domain" "staging" {
491
+ count = var.staging_domain != "" ? 1 : 0
492
+
493
+ account_id = var.cloudflare_account_id
494
+ project_name = cloudflare_pages_project.content.name
495
+ domain = var.staging_domain
496
+ }
497
+
498
+ # --- Workers(file-upload)---
499
+ # Workers スクリプトは TypeScript のビルドが必要なため、
500
+ # wrangler deploy で個別にデプロイすることを推奨。
501
+ # Terraform で管理する場合は以下のコメントを外し、
502
+ # ビルド済み JS バンドルのパスを workers_script_path に設定する。
503
+ #
504
+ # resource "cloudflare_workers_script" "file_upload" {
505
+ # account_id = var.cloudflare_account_id
506
+ # name = "frelio-file-upload"
507
+ # content = file(var.workers_script_path)
508
+ # module = true
509
+ #
510
+ # r2_bucket_binding {
511
+ # name = "R2"
512
+ # bucket_name = cloudflare_r2_bucket.files.name
513
+ # }
514
+ #
515
+ # plain_text_binding {
516
+ # name = "R2_PUBLIC_URL"
517
+ # text = var.r2_public_url
518
+ # }
519
+ # }
520
+
521
+ # --- Cloudflare Access(ステージング保護)---
522
+ # ステージングサイトにアクセス制限を設ける場合はコメントを外す。
523
+ #
524
+ # resource "cloudflare_zero_trust_access_application" "staging" {
525
+ # account_id = var.cloudflare_account_id
526
+ # name = "Frelio Staging"
527
+ # domain = var.staging_domain
528
+ # type = "self_hosted"
529
+ # session_duration = "24h"
530
+ # auto_redirect_to_identity = false
531
+ # }
532
+ #
533
+ # resource "cloudflare_zero_trust_access_policy" "staging_allow" {
534
+ # account_id = var.cloudflare_account_id
535
+ # application_id = cloudflare_zero_trust_access_application.staging.id
536
+ # name = "Allow members"
537
+ # decision = "allow"
538
+ # precedence = 1
539
+ #
540
+ # include {
541
+ # email = ["admin@example.com"]
542
+ # }
543
+ # }
544
+ `;
545
+ }
546
+ export function generateTerraformOutputs() {
547
+ return `output "r2_bucket_name" {
548
+ value = cloudflare_r2_bucket.files.name
549
+ description = "R2 バケット名"
550
+ }
551
+
552
+ output "content_pages_url" {
553
+ value = "\${cloudflare_pages_project.content.name}.pages.dev"
554
+ description = "コンテンツ配信 Pages URL"
555
+ }
556
+
557
+ output "admin_pages_url" {
558
+ value = "\${cloudflare_pages_project.admin.name}.pages.dev"
559
+ description = "管理画面 Pages URL"
560
+ }
561
+
562
+ output "production_domain" {
563
+ value = length(cloudflare_pages_domain.production) > 0 ? cloudflare_pages_domain.production[0].domain : null
564
+ description = "本番カスタムドメイン"
565
+ }
566
+
567
+ output "admin_domain" {
568
+ value = length(cloudflare_pages_domain.admin) > 0 ? cloudflare_pages_domain.admin[0].domain : null
569
+ description = "管理画面カスタムドメイン"
570
+ }
571
+ `;
572
+ }
573
+ export function generateTerraformTfvarsExample(config) {
574
+ return `# Cloudflare
575
+ cloudflare_account_id = "<YOUR_CLOUDFLARE_ACCOUNT_ID>"
576
+ cloudflare_api_token = "<YOUR_CLOUDFLARE_API_TOKEN>"
577
+
578
+ # GitHub OAuth
579
+ github_client_id = "${config.githubClientId || '<YOUR_GITHUB_CLIENT_ID>'}"
580
+ github_client_secret = "<YOUR_GITHUB_CLIENT_SECRET>"
581
+
582
+ # リソース名
583
+ r2_bucket_name = "${config.r2BucketName}"
584
+ pages_project_name = "${config.pagesProjectName}"
585
+ admin_pages_project_name = "${config.adminPagesProjectName}"
586
+ r2_public_url = "${config.r2PublicUrl || '<YOUR_R2_PUBLIC_URL>'}"
587
+
588
+ # カスタムドメイン(不要なら空文字のまま)
589
+ production_domain = "${config.productionUrl ? new URL(config.productionUrl).hostname : ''}"
590
+ admin_domain = ""
591
+ staging_domain = "${config.stagingDomain}"
592
+ `;
593
+ }
594
+ export function generateTerraformReadme() {
595
+ return `# Terraform - Cloudflare インフラ管理
596
+
597
+ Frelio で使用する Cloudflare リソースを Terraform で管理します。
598
+
599
+ ## 前提条件
600
+
601
+ - [Terraform CLI](https://developer.hashicorp.com/terraform/install) >= 1.5.0
602
+ - Cloudflare API トークン(Pages + R2 の操作権限)
603
+ - Cloudflare Dashboard → My Profile → API Tokens → Create Token
604
+
605
+ ## セットアップ
606
+
607
+ \`\`\`bash
608
+ cd terraform
609
+
610
+ # 1. 変数ファイルを作成
611
+ cp terraform.tfvars.example terraform.tfvars
612
+ # terraform.tfvars を編集し、実際の値を入力
613
+
614
+ # 2. 初期化
615
+ terraform init
616
+
617
+ # 3. 変更確認
618
+ terraform plan
619
+
620
+ # 4. 適用
621
+ terraform apply
622
+ \`\`\`
623
+
624
+ ## 注意事項
625
+
626
+ - \`terraform.tfvars\` にはシークレットが含まれるため **git にコミットしないこと**
627
+ - シークレットは環境変数でも設定可能: \`TF_VAR_cloudflare_api_token\`, \`TF_VAR_github_client_secret\`
628
+ - Pages シークレットは write-only のため、\`terraform plan\` で常に差分が表示されます(正常動作)
629
+
630
+ ## 既存リソースのインポート
631
+
632
+ \`frelio init\`(wrangler ベース)で作成済みのリソースがある場合:
633
+
634
+ \`\`\`bash
635
+ # R2 バケット
636
+ terraform import cloudflare_r2_bucket.files <account_id>/<bucket_name>
637
+
638
+ # Pages プロジェクト(コンテンツ配信)
639
+ terraform import cloudflare_pages_project.content <account_id>/<project_name>
640
+
641
+ # Pages プロジェクト(管理画面)
642
+ terraform import cloudflare_pages_project.admin <account_id>/<admin_project_name>
643
+ \`\`\`
644
+
645
+ ## リモートステート(チーム運用向け)
646
+
647
+ デフォルトはローカルステートです。チームで運用する場合は \`providers.tf\` の
648
+ S3 バックエンド設定をコメント解除し、Cloudflare R2 をリモートバックエンドとして使用できます。
649
+ `;
650
+ }
327
651
  /**
328
652
  * ファイルを書き込む(ディレクトリがなければ作成)
329
653
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-time/frelio-cli",
3
- "version": "1.3.12",
3
+ "version": "1.4.0",
4
4
  "description": "Frelio CMS setup CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",