tayo 0.2.3 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a128e0d938a02268ef73d7c92f956d9f316923eaf1ba7c8827f416a738e2365f
4
- data.tar.gz: 6e8c5fa59202380639d5f44dc05ea1723b3df5018a15ae9ec56bb5acf2765f58
3
+ metadata.gz: 9389fdda0837a9e94f5b087790c5511401e4765d751dc44e9c6ddc13f4655de8
4
+ data.tar.gz: 213fa000584b7345d4f0b00382275716be28874e2e62d58e1930c0cdcd2ca2cc
5
5
  SHA512:
6
- metadata.gz: 4a08ad8b3b192257dbbae0cf19f007210f70e303d7b64727eb388d1381b45ac36d9b3ff9e2441490951c37eb7732da2cd0e4e14fa771fd0b3b58685f36f05490
7
- data.tar.gz: 53919de826cf6970b5cd96c9bbeda335c6346df981cd9f46b7790c8f5fb310baf2362b704477ee4a5d5f84d487260ec794ece693fc67bcebbe9d9278acd22943
6
+ metadata.gz: e41db82f8bd4fce234b619e85b27d226f0228119a06d08ad16436ad67d656f230fecd16a47340ef45ae726212ee240172fdd0e150cfba29ef5ef118c5da39499
7
+ data.tar.gz: 3d4f54694b9cac6f819b1eb3e81e3885e3c978735e6bacd0a20b32827c3a2e22a95e1e2f5a01636c3857e9a0770d912d97c4ab5f5a0f831711d2694f2d241b27
data/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@
4
4
 
5
5
  이 프로젝트는 [Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다.
6
6
 
7
+ ## [0.3.0] - 2025-01-11
8
+
9
+ ### 제거됨
10
+ - `tayo proxy` 명령어 제거 - Kamal이 자동으로 Kamal Proxy를 관리하므로 불필요
11
+
12
+ ### 개선됨
13
+ - **tayo cf**: Cloudflare 토큰 저장 및 재사용 기능
14
+ - 환경변수 `CLOUDFLARE_API_TOKEN` 지원
15
+ - `~/.config/tayo/cloudflare_token`에 토큰 자동 저장
16
+ - 저장된 토큰 유효성 검사 후 재사용
17
+ - **tayo cf**: 홈서버 정보 저장 및 재사용 기능
18
+ - `~/.config/tayo/server.yml`에 서버 정보 저장
19
+ - 저장된 정보 표시 후 사용 여부 확인
20
+ - **tayo gh**: deploy.yml 설정 버그 수정
21
+ - 주석 처리된 registry username/password 활성화 수정
22
+ - image 이름에서 ghcr.io 중복 제거
23
+ - **tayo gh**: .kamal/secrets 설정 개선
24
+ - `KAMAL_REGISTRY_PASSWORD=$(gh auth token)` 형식으로 동적 토큰 사용
25
+ - 주석 처리된 라인과 실제 설정 라인 구분
26
+
7
27
  ## [0.2.0] - 2024-12-28
8
28
 
9
29
  ### 추가됨
@@ -61,6 +81,7 @@
61
81
  - 도메인 Zone 자동 감지
62
82
  - DNS A 레코드 생성/업데이트
63
83
 
84
+ [0.3.0]: https://github.com/TeamMilestone/tayo/compare/v0.2.3...v0.3.0
64
85
  [0.2.0]: https://github.com/TeamMilestone/tayo/compare/v0.1.11...v0.2.0
65
86
  [0.1.11]: https://github.com/TeamMilestone/tayo/compare/v0.1.10...v0.1.11
66
87
  [0.1.10]: https://github.com/TeamMilestone/tayo/compare/v0.1.0...v0.1.10
data/README.md CHANGED
@@ -26,7 +26,7 @@ tayo init
26
26
  - **Bundle 설치**: 의존성을 설치합니다
27
27
  - **Linux 플랫폼 추가**: `x86_64-linux`와 `aarch64-linux` 플랫폼을 Gemfile.lock에 추가합니다
28
28
  - **Dockerfile 생성**: Rails 7 기본 Dockerfile이 없으면 생성합니다
29
- - **Welcome 페이지 생성**:
29
+ - **Welcome 페이지 생성**:
30
30
  - `app/controllers/welcome_controller.rb` 컨트롤러 생성
31
31
  - `app/views/welcome/index.html.erb` 뷰 파일 생성 (애니메이션이 있는 예쁜 랜딩 페이지)
32
32
  - `config/routes.rb`에 `root 'welcome#index'` 설정 추가
@@ -44,7 +44,7 @@ tayo gh
44
44
  이 명령어는 다음 작업들을 수행합니다:
45
45
 
46
46
  - **GitHub CLI 설치 확인**: `gh` 명령어가 설치되어 있는지 확인합니다
47
- - **GitHub 인증 확인**:
47
+ - **GitHub 인증 확인**:
48
48
  - GitHub에 로그인되어 있는지 확인
49
49
  - 필요한 권한(repo, read:org, write:packages) 확인
50
50
  - 권한이 없으면 브라우저에서 토큰 생성 페이지를 엽니다
@@ -56,12 +56,9 @@ tayo gh
56
56
  - **GitHub Container Registry 설정**:
57
57
  - Registry URL 생성: `ghcr.io/username/repository-name`
58
58
  - Docker로 자동 로그인 실행
59
- - **배포 설정 파일 생성**:
60
- - `config/deploy.yml` 파일 생성 또는 업데이트
61
- - 서버 IP, 도메인, 데이터베이스 등 설정 포함
62
- - **환경 변수 파일 준비**:
63
- - `.env.production` 파일 생성
64
- - `.gitignore`에 추가하여 보안 유지
59
+ - `.kamal/secrets`에 `KAMAL_REGISTRY_PASSWORD` 자동 설정
60
+ - **배포 설정 파일 업데이트**:
61
+ - `config/deploy.yml` 파일의 image, registry 설정 업데이트
65
62
 
66
63
  ### 3. `tayo cf` - Cloudflare DNS 설정
67
64
 
@@ -73,95 +70,57 @@ tayo cf
73
70
 
74
71
  이 명령어는 다음 작업들을 수행합니다:
75
72
 
76
- - **설정 파일 확인**: `config/deploy.yml` 파일에서 서버 IP와 도메인 정보를 읽습니다
77
73
  - **Cloudflare 인증**:
78
- - API 토큰 입력 요청 (처음 실행 시)
79
- - 토큰을 안전하게 저장 (macOS Keychain 사용)
80
- - **도메인 Zone 확인**:
81
- - Cloudflare 계정에서 도메인을 찾습니다
82
- - Zone ID를 자동으로 가져옵니다
74
+ - 저장된 토큰 확인 (`~/.config/tayo/cloudflare_token`)
75
+ - 환경변수 `CLOUDFLARE_API_TOKEN` 지원
76
+ - 토큰이 없거나 유효하지 않으면 생성 페이지 열고 입력 받음
77
+ - 유효한 토큰은 자동 저장 (다음 실행 시 재사용)
78
+ - **도메인 Zone 선택**:
79
+ - Cloudflare 계정의 도메인 목록 표시
80
+ - Zone ID 자동 가져오기
81
+ - **기존 DNS 레코드 표시**:
82
+ - 선택한 Zone의 A/CNAME 레코드 목록 표시
83
+ - **서비스 도메인 설정**:
84
+ - 루트 도메인 또는 서브도메인 선택
85
+ - **홈서버 연결 정보**:
86
+ - 저장된 정보가 있으면 확인 후 재사용 (`~/.config/tayo/server.yml`)
87
+ - 없으면 IP/도메인과 SSH 사용자 입력 받아 저장
83
88
  - **DNS 레코드 생성/업데이트**:
84
- - A 레코드 생성: 도메인을 서버 IP에 연결
89
+ - A 레코드 (IP) 또는 CNAME 레코드 (도메인) 자동 선택
85
90
  - 기존 레코드가 있으면 업데이트
86
- - Proxied 설정 (Cloudflare CDN 사용)
87
- - **설정 완료 확인**:
88
- - DNS 설정이 완료되면 성공 메시지 표시
89
- - 도메인으로 접속 가능함을 안내
91
+ - **deploy.yml 업데이트**:
92
+ - `servers.web` 호스트 설정
93
+ - `proxy` 섹션 (SSL 도메인) 설정
94
+ - `ssh.user` 설정
90
95
 
91
- ### 4. `tayo proxy` - 프록시 서버 설정 (고급)
96
+ ### 4. `tayo sqlite` - SQLite 최적화 (Solid Cable용)
92
97
 
93
- Traefik을 Docker로 설정하여 도메인 라우팅과 SSL을 자동 관리합니다.
98
+ Solid Cable과 SQLite를 함께 사용할 필요한 최적화 설정을 적용합니다.
94
99
 
95
100
  ```bash
96
- tayo proxy
101
+ tayo sqlite
97
102
  ```
98
103
 
99
104
  이 명령어는 다음 작업들을 수행합니다:
100
105
 
101
- - **Cloudflare API 설정**:
102
- - API 토큰 확인 검증 (`~/.tayo/cloudflare_token`)
103
- - 토큰이 없거나 만료되면 재입력 안내
104
- - 도메인 목록에서 멀티 선택 가능
105
-
106
- - **네트워크 구성**:
107
- - 공인 IP 자동 감지 (`curl ifconfig.me`)
108
- - 내부 네트워크 IP 감지
109
- - 포트포워딩 방식 선택:
110
- - 직접 연결: 80, 443 (기본)
111
- - 커스텀 포트: 8080, 8443 등 (80/443이 사용 중일 때)
112
-
113
- - **Docker 컨테이너 관리**:
114
- - **Traefik**: 리버스 프록시 서버로 80, 443 포트에서 실행
115
- - 도메인별 자동 라우팅 (File Provider 사용)
116
- - Let's Encrypt SSL 인증서 자동 발급 및 갱신 (ACME)
117
- - 호스트에서 실행 중인 서비스 연결 지원 (host.docker.internal)
118
- - Docker Compose로 관리
119
- - **Welcome Service**: 3000 포트에 기본 페이지 제공 (Rails 등 호스트 서비스가 없을 때만)
120
- - **대시보드**: http://localhost:8080 에서 Traefik 상태 및 라우팅 규칙 확인 (admin/admin)
121
-
122
- - **DNS 자동 설정**:
123
- - 선택한 도메인에 공인 IP를 A 레코드로 등록
124
- - 기존 레코드는 자동 업데이트
125
-
126
- - **프록시 아키텍처**:
127
- ```
128
- [인터넷] → [공유기] → [Traefik:80,443] → [호스트 앱:3000]
129
-
130
- [Docker Compose 관리]
131
- - traefik.yml (정적 설정)
132
- - dynamic.yml (동적 라우팅)
133
- - acme.json (SSL 인증서)
134
- ```
135
-
136
- **사용 시나리오:**
137
- - 홈서버에서 여러 도메인 호스팅
138
- - 자동 SSL 인증서 관리 (Let's Encrypt ACME)
139
- - 공유기 포트포워딩과 함께 사용
140
- - 기존 웹서버와 병행 운영 (커스텀 포트 사용)
141
- - Docker 컨테이너 및 호스트 서비스 모두 지원
142
-
143
- 자세한 아키텍처 문서: [doc/proxy-architecture.md](doc/proxy-architecture.md)
144
-
145
- 각 명령어는 단계별로 진행 상황을 표시하며, 오류가 발생하면 친절한 안내 메시지를 제공합니다.
106
+ - **SQLite WAL 모드 설정**: database.yml에 WAL 저널 모드 추가
107
+ - **연결 설정 최적화**: idle_timeout, checkout_timeout, pool 설정
146
108
 
147
109
  ## 사용 예시
148
110
 
149
- ### Rails 앱 배포 (기본)
111
+ ### Rails 앱 배포
150
112
  ```bash
151
113
  rails new myapp
152
114
  cd myapp
153
- bundle exec tayo init
154
- bundle exec tayo gh
155
- bundle exec tayo cf
115
+ tayo init
116
+ tayo gh
117
+ tayo cf
156
118
  bin/kamal setup
157
119
  ```
158
120
 
159
- ### 프록시 서버 설정 (고급)
160
- ```bash
161
- # 홈서버에 프록시 환경 구성
162
- tayo proxy
121
+ ## 저장되는 설정 파일
163
122
 
164
- # 여러 도메인을 서버에서 호스팅
165
- # SSL 인증서 자동 관리
166
- # 공유기 포트포워딩과 함께 사용
167
- ```
123
+ | 파일 | 용도 |
124
+ |------|------|
125
+ | `~/.config/tayo/cloudflare_token` | Cloudflare API 토큰 |
126
+ | `~/.config/tayo/server.yml` | 홈서버 연결 정보 (IP, SSH 사용자) |
data/lib/tayo/cli.rb CHANGED
@@ -5,7 +5,6 @@ require "colorize"
5
5
  require_relative "commands/init"
6
6
  require_relative "commands/gh"
7
7
  require_relative "commands/cf"
8
- require_relative "commands/proxy"
9
8
  require_relative "commands/sqlite"
10
9
 
11
10
  module Tayo
@@ -25,11 +24,6 @@ module Tayo
25
24
  Commands::Cf.new.execute
26
25
  end
27
26
 
28
- desc "proxy", "Traefik을 설정하여 도메인 라우팅과 SSL을 관리합니다"
29
- def proxy
30
- Commands::Proxy.new.execute
31
- end
32
-
33
27
  desc "sqlite", "SQLite + Solid Cable 최적화 설정을 적용합니다"
34
28
  def sqlite
35
29
  Commands::Sqlite.new.execute
@@ -5,20 +5,22 @@ require "tty-prompt"
5
5
  require "net/http"
6
6
  require "json"
7
7
  require "uri"
8
+ require "fileutils"
9
+ require "yaml"
8
10
 
9
11
  module Tayo
10
12
  module Commands
11
13
  class Cf
14
+ CLOUDFLARE_TOKEN_FILE = File.expand_path("~/.config/tayo/cloudflare_token")
15
+ SERVER_CONFIG_FILE = File.expand_path("~/.config/tayo/server.yml")
16
+
12
17
  def execute
13
18
  puts "☁️ Cloudflare DNS 설정을 시작합니다...".colorize(:green)
14
19
 
15
- # 1. Cloudflare 토큰 생성 페이지 열기 권한 안내
16
- open_token_creation_page
17
-
18
- # 2. 토큰 입력받기
19
- token = get_cloudflare_token
20
+ # 1. Cloudflare 인증 확인 (저장된 토큰 확인 또는 새로 입력)
21
+ token = check_cloudflare_auth
20
22
 
21
- # 3. Cloudflare API로 도메인 목록 조회 및 선택
23
+ # 2. Cloudflare API로 도메인 목록 조회 및 선택
22
24
  selected_zone = select_cloudflare_zone(token)
23
25
 
24
26
  # 4. 기존 레코드 목록 표시
@@ -67,13 +69,27 @@ module Tayo
67
69
  def get_server_info
68
70
  prompt = TTY::Prompt.new
69
71
 
70
- puts "\n🖥️ 홈서버 연결 정보를 입력합니다.".colorize(:yellow)
72
+ puts "\n🖥️ 홈서버 연결 정보를 확인합니다.".colorize(:yellow)
71
73
 
72
- server_address = prompt.ask("홈서버 IP 또는 도메인을 입력하세요:") do |q|
73
- q.validate(/\A.+\z/, "서버 정보를 입력해주세요")
74
- end
74
+ # 저장된 서버 정보 확인
75
+ saved_config = load_server_config
75
76
 
76
- ssh_user = prompt.ask("SSH 사용자 계정을 입력하세요:", default: "root")
77
+ if saved_config
78
+ puts "\n저장된 홈서버 정보:".colorize(:cyan)
79
+ puts " • 서버: #{saved_config['server_address']}".colorize(:white)
80
+ puts " • SSH 사용자: #{saved_config['ssh_user']}".colorize(:white)
81
+
82
+ if prompt.yes?("\n이 정보를 사용하시겠습니까?")
83
+ server_address = saved_config['server_address']
84
+ ssh_user = saved_config['ssh_user']
85
+ else
86
+ server_address, ssh_user = prompt_server_info(prompt)
87
+ save_server_config(server_address, ssh_user)
88
+ end
89
+ else
90
+ server_address, ssh_user = prompt_server_info(prompt)
91
+ save_server_config(server_address, ssh_user)
92
+ end
77
93
 
78
94
  # IP인지 도메인인지 판단
79
95
  is_ip = server_address.match?(/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/)
@@ -86,13 +102,79 @@ module Tayo
86
102
  }
87
103
  end
88
104
 
89
- def open_token_creation_page
105
+ def prompt_server_info(prompt)
106
+ server_address = prompt.ask("홈서버 IP 또는 도메인을 입력하세요:") do |q|
107
+ q.validate(/\A.+\z/, "서버 정보를 입력해주세요")
108
+ end
109
+
110
+ ssh_user = prompt.ask("SSH 사용자 계정을 입력하세요:", default: "root")
111
+
112
+ [server_address, ssh_user]
113
+ end
114
+
115
+ def load_server_config
116
+ return nil unless File.exist?(SERVER_CONFIG_FILE)
117
+
118
+ config = YAML.load_file(SERVER_CONFIG_FILE)
119
+ return nil unless config.is_a?(Hash)
120
+ return nil unless config['server_address'] && config['ssh_user']
121
+
122
+ config
123
+ rescue
124
+ nil
125
+ end
126
+
127
+ def save_server_config(server_address, ssh_user)
128
+ dir = File.dirname(SERVER_CONFIG_FILE)
129
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
130
+
131
+ config = {
132
+ 'server_address' => server_address,
133
+ 'ssh_user' => ssh_user
134
+ }
135
+
136
+ File.write(SERVER_CONFIG_FILE, config.to_yaml)
137
+ File.chmod(0600, SERVER_CONFIG_FILE)
138
+
139
+ puts "✅ 홈서버 정보가 저장되었습니다.".colorize(:green)
140
+ end
141
+
142
+ def check_cloudflare_auth
143
+ # 1. 환경변수에서 토큰 확인
144
+ token = ENV['CLOUDFLARE_API_TOKEN']
145
+ if token && !token.strip.empty?
146
+ if test_cloudflare_token(token.strip)
147
+ puts "✅ Cloudflare에 로그인되어 있습니다. (환경변수)".colorize(:green)
148
+ return token.strip
149
+ else
150
+ puts "⚠️ 환경변수의 Cloudflare 토큰이 유효하지 않습니다.".colorize(:yellow)
151
+ end
152
+ end
153
+
154
+ # 2. 저장된 파일에서 토큰 확인
155
+ if File.exist?(CLOUDFLARE_TOKEN_FILE)
156
+ token = File.read(CLOUDFLARE_TOKEN_FILE).strip
157
+ if !token.empty? && test_cloudflare_token(token)
158
+ puts "✅ Cloudflare에 로그인되어 있습니다.".colorize(:green)
159
+ return token
160
+ else
161
+ puts "⚠️ 저장된 Cloudflare 토큰이 유효하지 않습니다.".colorize(:yellow)
162
+ end
163
+ end
164
+
165
+ # 3. 새 토큰 요청
166
+ request_new_cloudflare_token
167
+ end
168
+
169
+ def request_new_cloudflare_token
170
+ prompt = TTY::Prompt.new
171
+
90
172
  puts "\n🔑 Cloudflare API 토큰이 필요합니다.".colorize(:yellow)
91
173
  puts "토큰 생성 페이지를 엽니다...".colorize(:cyan)
92
-
174
+
93
175
  # Cloudflare API 토큰 생성 페이지 열기
94
176
  system("open 'https://dash.cloudflare.com/profile/api-tokens'")
95
-
177
+
96
178
  puts "\n다음 권한으로 토큰을 생성해주세요:".colorize(:yellow)
97
179
  puts ""
98
180
  puts "한국어 화면:".colorize(:gray)
@@ -103,28 +185,37 @@ module Tayo
103
185
  puts "• Zone → DNS → Edit".colorize(:white)
104
186
  puts " (Zone Resources: Select 'All zones')".colorize(:gray)
105
187
  puts ""
106
- end
107
188
 
108
- def get_cloudflare_token
109
- prompt = TTY::Prompt.new
110
-
111
189
  token = prompt.mask("생성된 Cloudflare API 토큰을 붙여넣으세요:")
112
-
190
+
113
191
  if token.nil? || token.strip.empty?
114
192
  puts "❌ 토큰이 입력되지 않았습니다.".colorize(:red)
115
193
  exit 1
116
194
  end
117
-
118
- # 토큰 유효성 간단 확인
119
- if test_cloudflare_token(token.strip)
120
- puts "✅ 토큰이 확인되었습니다.".colorize(:green)
121
- return token.strip
195
+
196
+ token = token.strip
197
+
198
+ # 토큰 유효성 확인
199
+ if test_cloudflare_token(token)
200
+ save_cloudflare_token(token)
201
+ puts "✅ 토큰이 확인되고 저장되었습니다.".colorize(:green)
202
+ return token
122
203
  else
123
204
  puts "❌ 토큰이 올바르지 않거나 권한이 부족합니다.".colorize(:red)
124
205
  exit 1
125
206
  end
126
207
  end
127
208
 
209
+ def save_cloudflare_token(token)
210
+ # 디렉토리 생성
211
+ dir = File.dirname(CLOUDFLARE_TOKEN_FILE)
212
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
213
+
214
+ # 토큰 저장 (파일 권한 600으로 설정)
215
+ File.write(CLOUDFLARE_TOKEN_FILE, token)
216
+ File.chmod(0600, CLOUDFLARE_TOKEN_FILE)
217
+ end
218
+
128
219
  def test_cloudflare_token(token)
129
220
  uri = URI('https://api.cloudflare.com/client/v4/user/tokens/verify')
130
221
  http = Net::HTTP.new(uri.host, uri.port)
@@ -373,13 +464,29 @@ module Tayo
373
464
 
374
465
  content = File.read(config_file)
375
466
 
376
- # proxy.host 설정 업데이트
377
- if content.include?("proxy:")
378
- content.gsub!(/(\s+host:\s+).*$/, "\\1#{final_domain}")
467
+ # proxy 섹션 설정
468
+ # 1. 활성화된 proxy 섹션이 있는지 확인 (줄 시작이 'proxy:'인 경우)
469
+ # 2. 주석 처리된 proxy 섹션이 있으면 활성화
470
+ # 3. 없으면 새로 추가
471
+ if content.match?(/^proxy:\s*$/m)
472
+ # 활성화된 proxy 섹션이 있음 - host 값만 업데이트
473
+ content.gsub!(/^(proxy:\s*\n\s*ssl:\s*true\s*\n\s*host:\s*)\S+/, "\\1#{final_domain}")
474
+ elsif content.match?(/^# proxy:\s*$/m)
475
+ # 주석 처리된 proxy 섹션이 있음 - 주석 해제하고 값 설정
476
+ # 주의: m 플래그 없이 사용하여 .가 개행을 매칭하지 않도록 함
477
+ content.gsub!(
478
+ /^# proxy:\s*\n#\s+ssl:\s*true\s*\n#\s+host:\s*\S+/,
479
+ "proxy:\n ssl: true\n host: #{final_domain}"
480
+ )
379
481
  else
380
- # proxy 섹션이 없으면 추가
381
- proxy_config = "\n# Proxy configuration\nproxy:\n ssl: true\n host: #{final_domain}\n"
382
- content += proxy_config
482
+ # proxy 섹션이 없음 - registry 섹션 앞에 추가
483
+ proxy_config = "proxy:\n ssl: true\n host: #{final_domain}\n\n"
484
+ if content.match?(/^# Where you keep your container images/m)
485
+ content.gsub!(/^# Where you keep your container images/, "#{proxy_config}# Where you keep your container images")
486
+ else
487
+ # registry 섹션 앞에 추가
488
+ content.gsub!(/^registry:/, "#{proxy_config}registry:")
489
+ end
383
490
  end
384
491
 
385
492
  # servers 설정 업데이트