tayo 0.1.13 → 0.2.2

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.
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "colorize"
4
+ require "fileutils"
5
+
6
+ module Tayo
7
+ module Proxy
8
+ class WelcomeService
9
+ def initialize
10
+ @docker = DockerManager.new
11
+ end
12
+
13
+ def ensure_running
14
+ # 호스트에서 3000 포트 서비스 확인 (Docker 제외)
15
+ if host_port_in_use?(3000)
16
+ puts "✅ 3000 포트에 호스트 서비스(Rails 등)가 실행 중입니다.".colorize(:green)
17
+
18
+ # 기존 Welcome 컨테이너가 있다면 중지
19
+ if @docker.container_running?("tayo-welcome")
20
+ puts "🛑 기존 Welcome 서비스를 중지합니다...".colorize(:yellow)
21
+ @docker.stop_container("tayo-welcome")
22
+ end
23
+ return
24
+ end
25
+
26
+ # Docker 컨테이너로 3000 포트가 사용 중인 경우
27
+ if @docker.container_running?("tayo-welcome")
28
+ puts "✅ Welcome 서비스가 이미 실행 중입니다.".colorize(:green)
29
+ return
30
+ end
31
+
32
+ puts "\n🚀 3000 포트에 Welcome 서비스를 시작합니다...".colorize(:yellow)
33
+ puts " (Rails 서버를 시작하면 자동으로 중지됩니다)".colorize(:gray)
34
+
35
+ prepare_welcome_files
36
+ build_image
37
+ start_container
38
+
39
+ puts "✅ Welcome 서비스가 시작되었습니다.".colorize(:green)
40
+ end
41
+
42
+ private
43
+
44
+ def host_port_in_use?(port)
45
+ # macOS와 Linux에서 호스트 포트 확인 (Docker 컨테이너 제외)
46
+ if RUBY_PLATFORM.include?("darwin")
47
+ # macOS: lsof 사용, Docker 프로세스 제외
48
+ output = `lsof -i :#{port} -sTCP:LISTEN 2>/dev/null | grep -v docker | grep -v com.docke | grep -v tayo-welcome`.strip
49
+ !output.empty?
50
+ else
51
+ # Linux: netstat 또는 ss 사용
52
+ output = `netstat -tln 2>/dev/null | grep ":#{port} " | grep -v docker`.strip
53
+ if output.empty?
54
+ output = `ss -tln 2>/dev/null | grep ":#{port} " | grep -v docker`.strip
55
+ end
56
+ !output.empty?
57
+ end
58
+ end
59
+
60
+ def template_dir
61
+ File.expand_path("../../../templates/welcome", __FILE__)
62
+ end
63
+
64
+ def prepare_welcome_files
65
+ puts "📁 Welcome 서비스 파일을 준비합니다...".colorize(:yellow)
66
+
67
+ # 템플릿 디렉토리 생성
68
+ FileUtils.mkdir_p(template_dir)
69
+
70
+ # Dockerfile 생성
71
+ create_dockerfile
72
+
73
+ # index.html 생성
74
+ create_index_html
75
+ end
76
+
77
+ def create_dockerfile
78
+ dockerfile_content = <<~DOCKERFILE
79
+ FROM nginx:alpine
80
+
81
+ # Nginx 설정
82
+ RUN echo 'server { listen 80; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ =404; } }' > /etc/nginx/conf.d/default.conf
83
+
84
+ # HTML 파일 복사
85
+ COPY index.html /usr/share/nginx/html/
86
+
87
+ # 헬스체크
88
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
89
+ CMD wget --no-verbose --tries=1 --spider http://localhost || exit 1
90
+
91
+ EXPOSE 80
92
+
93
+ CMD ["nginx", "-g", "daemon off;"]
94
+ DOCKERFILE
95
+
96
+ File.write(File.join(template_dir, "Dockerfile"), dockerfile_content)
97
+ end
98
+
99
+ def create_index_html
100
+ html_content = <<~HTML
101
+ <!DOCTYPE html>
102
+ <html lang="ko">
103
+ <head>
104
+ <meta charset="UTF-8">
105
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
106
+ <title>Tayo Proxy - Welcome</title>
107
+ <style>
108
+ * {
109
+ margin: 0;
110
+ padding: 0;
111
+ box-sizing: border-box;
112
+ }
113
+
114
+ body {
115
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
116
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
117
+ min-height: 100vh;
118
+ display: flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ color: white;
122
+ padding: 20px;
123
+ }
124
+
125
+ .container {
126
+ text-align: center;
127
+ max-width: 600px;
128
+ animation: fadeIn 1s ease-in;
129
+ }
130
+
131
+ @keyframes fadeIn {
132
+ from { opacity: 0; transform: translateY(-20px); }
133
+ to { opacity: 1; transform: translateY(0); }
134
+ }
135
+
136
+ .logo {
137
+ font-size: 5em;
138
+ margin-bottom: 20px;
139
+ animation: bounce 2s infinite;
140
+ }
141
+
142
+ @keyframes bounce {
143
+ 0%, 100% { transform: translateY(0); }
144
+ 50% { transform: translateY(-10px); }
145
+ }
146
+
147
+ h1 {
148
+ font-size: 3em;
149
+ margin-bottom: 20px;
150
+ font-weight: 700;
151
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
152
+ }
153
+
154
+ .subtitle {
155
+ font-size: 1.3em;
156
+ opacity: 0.95;
157
+ margin-bottom: 30px;
158
+ line-height: 1.5;
159
+ }
160
+
161
+ .status {
162
+ background: rgba(255, 255, 255, 0.2);
163
+ border-radius: 10px;
164
+ padding: 20px;
165
+ backdrop-filter: blur(10px);
166
+ margin-top: 30px;
167
+ }
168
+
169
+ .status-item {
170
+ display: flex;
171
+ justify-content: space-between;
172
+ padding: 10px 0;
173
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
174
+ }
175
+
176
+ .status-item:last-child {
177
+ border-bottom: none;
178
+ }
179
+
180
+ .status-label {
181
+ font-weight: 600;
182
+ }
183
+
184
+ .status-value {
185
+ opacity: 0.9;
186
+ }
187
+
188
+ .status-ok {
189
+ color: #4ade80;
190
+ }
191
+
192
+ .info-box {
193
+ background: rgba(255, 255, 255, 0.1);
194
+ border-radius: 8px;
195
+ padding: 15px;
196
+ margin-top: 30px;
197
+ font-size: 0.9em;
198
+ opacity: 0.85;
199
+ }
200
+
201
+ .footer {
202
+ margin-top: 50px;
203
+ font-size: 0.85em;
204
+ opacity: 0.7;
205
+ }
206
+
207
+ .footer a {
208
+ color: white;
209
+ text-decoration: none;
210
+ border-bottom: 1px solid rgba(255, 255, 255, 0.3);
211
+ transition: border-color 0.3s;
212
+ }
213
+
214
+ .footer a:hover {
215
+ border-bottom-color: white;
216
+ }
217
+ </style>
218
+ </head>
219
+ <body>
220
+ <div class="container">
221
+ <div class="logo">🚀</div>
222
+ <h1>Tayo Proxy</h1>
223
+ <p class="subtitle">
224
+ 홈서버 프록시 서비스가 정상적으로 작동 중입니다<br>
225
+ Your home server proxy is running successfully
226
+ </p>
227
+
228
+ <div class="status">
229
+ <div class="status-item">
230
+ <span class="status-label">프록시 상태</span>
231
+ <span class="status-value status-ok">✓ 활성</span>
232
+ </div>
233
+ <div class="status-item">
234
+ <span class="status-label">Kamal Proxy</span>
235
+ <span class="status-value status-ok">✓ 실행 중</span>
236
+ </div>
237
+ <div class="status-item">
238
+ <span class="status-label">Caddy Server</span>
239
+ <span class="status-value status-ok">✓ 실행 중</span>
240
+ </div>
241
+ <div class="status-item">
242
+ <span class="status-label">SSL/TLS</span>
243
+ <span class="status-value status-ok">✓ 준비됨</span>
244
+ </div>
245
+ </div>
246
+
247
+ <div class="info-box">
248
+ <strong>💡 다음 단계:</strong><br>
249
+ 이제 실제 애플리케이션을 3000 포트에 배포하면<br>
250
+ 이 페이지 대신 애플리케이션이 표시됩니다.
251
+ </div>
252
+
253
+ <div class="footer">
254
+ <p>
255
+ Powered by <a href="https://github.com/TeamMilestone/tayo" target="_blank">Tayo</a> |
256
+ <a href="https://kamal-deploy.org" target="_blank">Kamal</a> |
257
+ <a href="https://caddyserver.com" target="_blank">Caddy</a>
258
+ </p>
259
+ </div>
260
+ </div>
261
+
262
+ <script>
263
+ // 현재 시간 표시 (옵션)
264
+ const updateTime = () => {
265
+ const now = new Date();
266
+ const timeString = now.toLocaleTimeString('ko-KR');
267
+ // 시간 표시 기능이 필요한 경우 활성화
268
+ };
269
+ setInterval(updateTime, 1000);
270
+ updateTime();
271
+ </script>
272
+ </body>
273
+ </html>
274
+ HTML
275
+
276
+ File.write(File.join(template_dir, "index.html"), html_content)
277
+ end
278
+
279
+ def build_image
280
+ puts "🔨 Docker 이미지를 빌드합니다...".colorize(:yellow)
281
+
282
+ cmd = "docker build -t tayo-welcome:latest #{template_dir}"
283
+
284
+ if system(cmd)
285
+ puts "✅ Docker 이미지 빌드가 완료되었습니다.".colorize(:green)
286
+ else
287
+ puts "❌ Docker 이미지 빌드에 실패했습니다.".colorize(:red)
288
+ exit 1
289
+ end
290
+ end
291
+
292
+ def start_container
293
+ puts "🚀 Welcome 컨테이너를 시작합니다...".colorize(:yellow)
294
+
295
+ # 기존 컨테이너가 있다면 제거
296
+ if @docker.container_exists?("tayo-welcome")
297
+ @docker.stop_container("tayo-welcome")
298
+ end
299
+
300
+ # 네트워크 확인
301
+ network = @docker.create_network_if_not_exists("tayo-proxy")
302
+
303
+ # Welcome 컨테이너 실행
304
+ cmd = <<~DOCKER
305
+ docker run -d \
306
+ --name tayo-welcome \
307
+ --network #{network} \
308
+ -p 3000:80 \
309
+ --restart unless-stopped \
310
+ tayo-welcome:latest
311
+ DOCKER
312
+
313
+ if system(cmd)
314
+ puts "✅ Welcome 서비스가 포트 3000에서 시작되었습니다.".colorize(:green)
315
+
316
+ # 서비스 확인
317
+ sleep 2
318
+ check_service_health
319
+ else
320
+ puts "❌ Welcome 서비스 시작에 실패했습니다.".colorize(:red)
321
+ exit 1
322
+ end
323
+ end
324
+
325
+ def check_service_health
326
+ # curl로 서비스 확인
327
+ response = `curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null`.strip
328
+
329
+ if response == "200"
330
+ puts "✅ Welcome 서비스가 정상적으로 응답합니다.".colorize(:green)
331
+ else
332
+ puts "⚠️ Welcome 서비스 응답 확인 중... (HTTP #{response})".colorize(:yellow)
333
+ end
334
+ end
335
+ end
336
+ end
337
+ end
data/lib/tayo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tayo
4
- VERSION = "0.1.13"
4
+ VERSION = "0.2.2"
5
5
  end
@@ -0,0 +1,14 @@
1
+ FROM nginx:alpine
2
+
3
+ # Nginx 설정
4
+ RUN echo 'server { listen 80; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ =404; } }' > /etc/nginx/conf.d/default.conf
5
+
6
+ # HTML 파일 복사
7
+ COPY index.html /usr/share/nginx/html/
8
+
9
+ # 헬스체크
10
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD wget --no-verbose --tries=1 --spider http://localhost || exit 1
11
+
12
+ EXPOSE 80
13
+
14
+ CMD ["nginx", "-g", "daemon off;"]
@@ -0,0 +1,173 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Tayo Proxy - Welcome</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ color: white;
22
+ padding: 20px;
23
+ }
24
+
25
+ .container {
26
+ text-align: center;
27
+ max-width: 600px;
28
+ animation: fadeIn 1s ease-in;
29
+ }
30
+
31
+ @keyframes fadeIn {
32
+ from { opacity: 0; transform: translateY(-20px); }
33
+ to { opacity: 1; transform: translateY(0); }
34
+ }
35
+
36
+ .logo {
37
+ font-size: 5em;
38
+ margin-bottom: 20px;
39
+ animation: bounce 2s infinite;
40
+ }
41
+
42
+ @keyframes bounce {
43
+ 0%, 100% { transform: translateY(0); }
44
+ 50% { transform: translateY(-10px); }
45
+ }
46
+
47
+ h1 {
48
+ font-size: 3em;
49
+ margin-bottom: 20px;
50
+ font-weight: 700;
51
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
52
+ }
53
+
54
+ .subtitle {
55
+ font-size: 1.3em;
56
+ opacity: 0.95;
57
+ margin-bottom: 30px;
58
+ line-height: 1.5;
59
+ }
60
+
61
+ .status {
62
+ background: rgba(255, 255, 255, 0.2);
63
+ border-radius: 10px;
64
+ padding: 20px;
65
+ backdrop-filter: blur(10px);
66
+ margin-top: 30px;
67
+ }
68
+
69
+ .status-item {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ padding: 10px 0;
73
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
74
+ }
75
+
76
+ .status-item:last-child {
77
+ border-bottom: none;
78
+ }
79
+
80
+ .status-label {
81
+ font-weight: 600;
82
+ }
83
+
84
+ .status-value {
85
+ opacity: 0.9;
86
+ }
87
+
88
+ .status-ok {
89
+ color: #4ade80;
90
+ }
91
+
92
+ .info-box {
93
+ background: rgba(255, 255, 255, 0.1);
94
+ border-radius: 8px;
95
+ padding: 15px;
96
+ margin-top: 30px;
97
+ font-size: 0.9em;
98
+ opacity: 0.85;
99
+ }
100
+
101
+ .footer {
102
+ margin-top: 50px;
103
+ font-size: 0.85em;
104
+ opacity: 0.7;
105
+ }
106
+
107
+ .footer a {
108
+ color: white;
109
+ text-decoration: none;
110
+ border-bottom: 1px solid rgba(255, 255, 255, 0.3);
111
+ transition: border-color 0.3s;
112
+ }
113
+
114
+ .footer a:hover {
115
+ border-bottom-color: white;
116
+ }
117
+ </style>
118
+ </head>
119
+ <body>
120
+ <div class="container">
121
+ <div class="logo">🚀</div>
122
+ <h1>Tayo Proxy</h1>
123
+ <p class="subtitle">
124
+ 홈서버 프록시 서비스가 정상적으로 작동 중입니다<br>
125
+ Your home server proxy is running successfully
126
+ </p>
127
+
128
+ <div class="status">
129
+ <div class="status-item">
130
+ <span class="status-label">프록시 상태</span>
131
+ <span class="status-value status-ok">✓ 활성</span>
132
+ </div>
133
+ <div class="status-item">
134
+ <span class="status-label">Kamal Proxy</span>
135
+ <span class="status-value status-ok">✓ 실행 중</span>
136
+ </div>
137
+ <div class="status-item">
138
+ <span class="status-label">Caddy Server</span>
139
+ <span class="status-value status-ok">✓ 실행 중</span>
140
+ </div>
141
+ <div class="status-item">
142
+ <span class="status-label">SSL/TLS</span>
143
+ <span class="status-value status-ok">✓ 준비됨</span>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="info-box">
148
+ <strong>💡 다음 단계:</strong><br>
149
+ 이제 실제 애플리케이션을 3000 포트에 배포하면<br>
150
+ 이 페이지 대신 애플리케이션이 표시됩니다.
151
+ </div>
152
+
153
+ <div class="footer">
154
+ <p>
155
+ Powered by <a href="https://github.com/TeamMilestone/tayo" target="_blank">Tayo</a> |
156
+ <a href="https://kamal-deploy.org" target="_blank">Kamal</a> |
157
+ <a href="https://caddyserver.com" target="_blank">Caddy</a>
158
+ </p>
159
+ </div>
160
+ </div>
161
+
162
+ <script>
163
+ // 현재 시간 표시 (옵션)
164
+ const updateTime = () => {
165
+ const now = new Date();
166
+ const timeString = now.toLocaleTimeString('ko-KR');
167
+ // 시간 표시 기능이 필요한 경우 활성화
168
+ };
169
+ setInterval(updateTime, 1000);
170
+ updateTime();
171
+ </script>
172
+ </body>
173
+ </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tayo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - 이원섭wonsup Lee/Alfonso
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.23'
68
+ - !ruby/object:Gem::Dependency
69
+ name: logger
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.6'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.6'
68
82
  description: Tayo is a deployment tool for Rails applications to home servers using
69
83
  GitHub Container Registry and Cloudflare CLI.
70
84
  email:
@@ -76,22 +90,26 @@ extra_rdoc_files: []
76
90
  files:
77
91
  - ".DS_Store"
78
92
  - CHANGELOG.md
79
- - CLAUDE.md
80
93
  - README.md
81
94
  - Rakefile
82
95
  - exe/tayo
83
96
  - lib/tayo.rb
84
97
  - lib/tayo/cli.rb
85
- - lib/tayo/commands/base.rb
86
98
  - lib/tayo/commands/cf.rb
87
99
  - lib/tayo/commands/gh.rb
88
100
  - lib/tayo/commands/init.rb
89
- - lib/tayo/commands/sqlite.rb
101
+ - lib/tayo/commands/proxy.rb
90
102
  - lib/tayo/dockerfile_modifier.rb
103
+ - lib/tayo/proxy/cloudflare_client.rb
104
+ - lib/tayo/proxy/docker_manager.rb
105
+ - lib/tayo/proxy/network_config.rb
106
+ - lib/tayo/proxy/traefik_config.rb
107
+ - lib/tayo/proxy/welcome_service.rb
91
108
  - lib/tayo/version.rb
109
+ - lib/templates/welcome/Dockerfile
110
+ - lib/templates/welcome/index.html
92
111
  - pkg/homebody-0.1.0.gem
93
112
  - repomix-output.xml
94
- - scripts/setup_rubygems_key.sh
95
113
  - sig/tayo.rbs
96
114
  homepage: https://github.com/TeamMilestone/tayo
97
115
  licenses: []
@@ -113,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
131
  - !ruby/object:Gem::Version
114
132
  version: '0'
115
133
  requirements: []
116
- rubygems_version: 3.6.9
134
+ rubygems_version: 3.7.1
117
135
  specification_version: 4
118
136
  summary: Rails deployment tool for home servers
119
137
  test_files: []
data/CLAUDE.md DELETED
@@ -1,58 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
- Tayo is a Ruby gem that simplifies Rails app deployment to home servers using GitHub Container Registry and Cloudflare.
7
-
8
- ## Common Development Commands
9
-
10
- ### Testing
11
- ```bash
12
- # Run all tests
13
- rake test
14
-
15
- # Run specific test file
16
- ruby -Ilib:test test/dockerfile_modifier_test.rb
17
- ```
18
-
19
- ### Building and Installing
20
- ```bash
21
- # Build gem
22
- rake build
23
-
24
- # Install locally
25
- rake install
26
-
27
- # Release to RubyGems.org
28
- rake release
29
- ```
30
-
31
- ## Architecture
32
-
33
- ### Core Structure
34
- - `lib/tayo/cli.rb` - Thor-based CLI entry point
35
- - `lib/tayo/commands/` - Command modules:
36
- - `init.rb` - Rails project initialization with Docker setup
37
- - `gh.rb` - GitHub repository and Container Registry configuration
38
- - `cf.rb` - Cloudflare DNS configuration
39
- - `lib/tayo/dockerfile_modifier.rb` - Handles bootsnap removal from Dockerfiles
40
-
41
- ### Key Patterns
42
- 1. **Command Structure**: Each command is a separate module under `Commands`
43
- 2. **Error Handling**: Use colorized Korean messages for user-friendly output
44
- 3. **Security**: Store sensitive tokens with 600 permissions, use macOS Keychain for Cloudflare tokens
45
- 4. **Git Integration**: Auto-commit after each major step with descriptive messages
46
- 5. **User Interaction**: Use TTY::Prompt for interactive configuration
47
-
48
- ### Workflow
49
- The typical usage flow:
50
- 1. `tayo init` - Sets up Rails project with Docker
51
- 2. `tayo gh` - Configures GitHub repository and Container Registry
52
- 3. `tayo cf` - Sets up Cloudflare DNS
53
- 4. `bin/kamal setup` - Deploys the application
54
-
55
- ### Testing Approach
56
- - Uses Minitest framework
57
- - Tests focus on unit testing individual components
58
- - DockerfileModifier has comprehensive test coverage
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Tayo
4
- module Commands
5
- class Base
6
- private
7
-
8
- def in_rails_project?
9
- File.exist?('Gemfile') && File.exist?('config/application.rb')
10
- end
11
- end
12
- end
13
- end