3v324v23 commited on
Commit
82f851a
·
1 Parent(s): 623538d

Полное перестроение контейнера с исправлением проблем прав доступа и добавлением прямого чтения property.json из API

Browse files
Files changed (2) hide show
  1. Dockerfile +28 -4
  2. app.py +146 -60
Dockerfile CHANGED
@@ -38,6 +38,9 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
38
  apt-get install -y nodejs && \
39
  npm install -g pnpm
40
 
 
 
 
41
  # Устанавливаем рабочую директорию
42
  WORKDIR /app
43
 
@@ -54,12 +57,24 @@ RUN go install github.com/go-task/task/v3/cmd/task@latest
54
  RUN mkdir -p /app/server/bin && \
55
  mkdir -p /app/agents && \
56
  mkdir -p /tmp/ten_agent && \
57
- mkdir -p /app/.pnpm-store
 
 
 
58
 
59
  # Копируем .env файл
60
  COPY .env /app/.env
 
 
 
 
 
61
 
62
- # Патчим файл конфигурации Next.js, чтобы перенаправлять запросы к дизайнеру на наш API
 
 
 
 
63
  RUN echo 'module.exports = { \
64
  async rewrites() { \
65
  return [ \
@@ -158,11 +173,20 @@ RUN echo '{\n\
158
  }' > /app/agents/chat_agent.json
159
 
160
  # Устанавливаем правильные права доступа для всех файлов
161
- RUN chmod -R 777 /app
 
 
 
 
 
162
 
163
  # Настраиваем стартовый скрипт
164
  COPY app.py /app/app.py
165
- RUN chmod +x /app/app.py
 
 
 
 
166
 
167
  # Открываем порты
168
  EXPOSE 7860 8080 3000
 
38
  apt-get install -y nodejs && \
39
  npm install -g pnpm
40
 
41
+ # Создаем пользователя tenuser с правильными правами
42
+ RUN useradd -m -u 1000 -s /bin/bash tenuser
43
+
44
  # Устанавливаем рабочую директорию
45
  WORKDIR /app
46
 
 
57
  RUN mkdir -p /app/server/bin && \
58
  mkdir -p /app/agents && \
59
  mkdir -p /tmp/ten_agent && \
60
+ mkdir -p /app/.pnpm-store && \
61
+ mkdir -p /app/backup && \
62
+ chown -R tenuser:tenuser /app && \
63
+ chown -R tenuser:tenuser /tmp/ten_agent
64
 
65
  # Копируем .env файл
66
  COPY .env /app/.env
67
+ RUN chown tenuser:tenuser /app/.env
68
+
69
+ # Патчим файлы для поддержки чтения графов
70
+ # 1. Патчим метод чтения графов в API сервере для прямого чтения из файла property.json
71
+ RUN echo 'package internal\n\nimport (\n\t"encoding/json"\n\t"fmt"\n\t"io/ioutil"\n\t"os"\n\t"path/filepath"\n)\n\n// GraphInfo представляет информацию о графе\ntype GraphInfo struct {\n\tName string `json:"name"`\n\tDescription string `json:"description"`\n\tFile string `json:"file"`\n}\n\n// PropertyJson структура файла property.json\ntype PropertyJson struct {\n\tTen map[string]interface{} `json:"_ten"`\n\tName string `json:"name"`\n\tVersion string `json:"version"`\n\tExtensions []string `json:"extensions"`\n\tDescription string `json:"description"`\n\tGraphs []GraphInfo `json:"graphs"`\n}\n\n// ReadGraphsFromPropertyJson читает список графов из property.json\nfunc ReadGraphsFromPropertyJson() ([]GraphInfo, error) {\n\tpropertyPath := filepath.Join("/app/agents", "property.json")\n\n\tdata, err := ioutil.ReadFile(propertyPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf("ошибка чтения property.json: %v", err)\n\t}\n\n\tvar property PropertyJson\n\terr = json.Unmarshal(data, &property)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf("ошибка разбора property.json: %v", err)\n\t}\n\n\treturn property.Graphs, nil\n}' > /app/server/internal/property_parser.go
72
 
73
+ # 2. Патчим обработчик для чтения графов из property.json
74
+ RUN sed -i 's/func (s \*HttpServer) handleGraphs/func oldHandleGraphs/g' /app/server/internal/http_server.go && \
75
+ echo 'func (s *HttpServer) handleGraphs(c *gin.Context) {\n\tgraphs, err := ReadGraphsFromPropertyJson()\n\tif err != nil {\n\t\ts.logger.Error(fmt.Sprintf("Failed to read graphs: %v", err))\n\t\tc.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})\n\t\treturn\n\t}\n\tc.JSON(http.StatusOK, graphs)\n}' >> /app/server/internal/http_server.go
76
+
77
+ # Патчим файл конфигурации Next.js для перенаправления запросов
78
  RUN echo 'module.exports = { \
79
  async rewrites() { \
80
  return [ \
 
173
  }' > /app/agents/chat_agent.json
174
 
175
  # Устанавливаем правильные права доступа для всех файлов
176
+ RUN chmod -R 777 /app && \
177
+ chown -R tenuser:tenuser /app/agents && \
178
+ chown -R tenuser:tenuser /app/server && \
179
+ find /app -type d -exec chmod 777 {} \; && \
180
+ find /app -type f -exec chmod 666 {} \; && \
181
+ chmod +x /app/server/bin/api
182
 
183
  # Настраиваем стартовый скрипт
184
  COPY app.py /app/app.py
185
+ RUN chmod +x /app/app.py && \
186
+ chown tenuser:tenuser /app/app.py
187
+
188
+ # Переключаемся на пользователя tenuser
189
+ USER tenuser
190
 
191
  # Открываем порты
192
  EXPOSE 7860 8080 3000
app.py CHANGED
@@ -9,6 +9,9 @@ import signal
9
  import threading
10
  import shutil
11
  import logging
 
 
 
12
 
13
  # Настройка логирования
14
  logging.basicConfig(level=logging.INFO,
@@ -17,12 +20,47 @@ logging.basicConfig(level=logging.INFO,
17
 
18
  logger = logging.getLogger('ten-agent')
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  def check_and_create_property_json():
21
  """Проверяет наличие property.json и создает его при необходимости"""
22
- property_path = Path("/app/agents/property.json")
23
-
24
- if not property_path.exists():
25
- logger.warning(f"{property_path} не найден, создаем файл...")
26
 
27
  property_data = {
28
  "_ten": {}, # Важное поле для TEN формата
@@ -45,22 +83,85 @@ def check_and_create_property_json():
45
  }
46
 
47
  # Проверяем и создаем директории
48
- property_path.parent.mkdir(parents=True, exist_ok=True)
49
 
50
- # Записываем файл
51
- with open(property_path, 'w') as f:
52
- json.dump(property_data, f, indent=2)
53
-
54
- logger.info(f"Файл {property_path} создан успешно")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  def check_files():
57
  """Проверяет и выводит информацию о важных файлах"""
58
  files_to_check = [
59
- "/app/agents/property.json",
60
- "/app/agents/manifest.json",
61
- "/app/agents/voice_agent.json",
62
- "/app/agents/chat_agent.json",
63
- "/app/server/bin/api"
64
  ]
65
 
66
  logger.info("=== Проверка критических файлов ===")
@@ -72,7 +173,7 @@ def check_files():
72
  logger.info(f"✅ {file_path} (размер: {size} байт)")
73
 
74
  # Если это JSON файл, выводим его содержимое
75
- if file_path.endswith('.json'):
76
  try:
77
  with open(file_path, 'r') as f:
78
  content = json.load(f)
@@ -85,43 +186,15 @@ def check_files():
85
  logger.error(f"❌ {file_path} (файл не найден)")
86
 
87
  logger.info("=== Проверка структуры директорий ===")
88
- logger.info("Содержимое /app/agents:")
89
- subprocess.run(["ls", "-la", "/app/agents"])
90
 
91
  logger.info("Проверка прав доступа:")
92
- subprocess.run(["stat", "/app/agents"])
93
- subprocess.run(["stat", "/app/agents/property.json"])
94
-
95
- def setup_mock_designer():
96
- """Настраивает мок-эндпоинты для дизайнера"""
97
- # Создаем файл с мок-ответом для запросов
98
- mock_dir = Path("/app/mock_responses")
99
- mock_dir.mkdir(exist_ok=True)
100
-
101
- reload_response = {
102
- "success": True,
103
- "packages": [
104
- {
105
- "name": "default",
106
- "description": "Default package",
107
- "graphs": [
108
- {"name": "Voice Agent", "description": "Voice Agent with OpenAI", "file": "voice_agent.json"},
109
- {"name": "Chat Agent", "description": "Simple Chat Agent", "file": "chat_agent.json"}
110
- ]
111
- }
112
- ]
113
- }
114
-
115
- with open(mock_dir / "reload_response.json", "w") as f:
116
- json.dump(reload_response, f, indent=2)
117
-
118
- logger.info("Мок-файлы для дизайнера созданы")
119
 
120
  def test_api():
121
  """Делает запрос к API для получения списка графов"""
122
- import urllib.request
123
- import urllib.error
124
-
125
  logger.info("=== Тестирование API ===")
126
  try:
127
  # Даем серверу время запуститься
@@ -135,6 +208,18 @@ def test_api():
135
  json_data = json.loads(data)
136
  if isinstance(json_data, list) and len(json_data) > 0:
137
  logger.info(f"API вернул {len(json_data)} графов")
 
 
 
 
 
 
 
 
 
 
 
 
138
  else:
139
  logger.warning("API вернул пустой список графов")
140
  except json.JSONDecodeError:
@@ -147,31 +232,31 @@ def test_api():
147
  def main():
148
  processes = []
149
  try:
150
- # Пути к исполняемым файлам
151
- api_binary = Path("/app/server/bin/api")
152
- playground_dir = Path("/app/playground")
153
-
154
  # Проверяем существование файлов
155
- if not api_binary.exists():
156
- logger.error(f"API binary не найден: {api_binary}")
157
  return 1
158
 
159
- if not playground_dir.exists():
160
- logger.error(f"Playground директория не найдена: {playground_dir}")
161
  return 1
162
 
 
 
 
 
163
  # Проверяем и создаем property.json
164
  check_and_create_property_json()
165
 
166
- # Создаем мок-файлы для дизайнера
167
- setup_mock_designer()
168
 
169
  # Проверка файлов перед запуском
170
  check_files()
171
 
172
  # Запускаем API сервер
173
  logger.info("Запуск TEN-Agent API сервера на порту 8080...")
174
- api_process = subprocess.Popen([str(api_binary)])
175
  processes.append(api_process)
176
 
177
  # Тестируем API
@@ -191,9 +276,10 @@ def main():
191
  os.environ["NEXT_PUBLIC_API_BASE_URL"] = "/api/agents"
192
  os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = "http://localhost:8080"
193
 
 
194
  playground_process = subprocess.Popen(
195
  ["pnpm", "dev"],
196
- cwd=str(playground_dir),
197
  env=os.environ
198
  )
199
  processes.append(playground_process)
 
9
  import threading
10
  import shutil
11
  import logging
12
+ import urllib.request
13
+ import urllib.error
14
+ import tempfile
15
 
16
  # Настройка логирования
17
  logging.basicConfig(level=logging.INFO,
 
20
 
21
  logger = logging.getLogger('ten-agent')
22
 
23
+ # Глобальные пути
24
+ AGENTS_DIR = Path("/app/agents")
25
+ PROPERTY_JSON = AGENTS_DIR / "property.json"
26
+ MANIFEST_JSON = AGENTS_DIR / "manifest.json"
27
+ VOICE_AGENT_JSON = AGENTS_DIR / "voice_agent.json"
28
+ CHAT_AGENT_JSON = AGENTS_DIR / "chat_agent.json"
29
+ API_BINARY = Path("/app/server/bin/api")
30
+ PLAYGROUND_DIR = Path("/app/playground")
31
+ BACKUP_DIR = Path("/app/backup")
32
+
33
+ def ensure_directory_permissions(directory_path):
34
+ """Обеспечиваем правильные разрешения для директории"""
35
+ directory = Path(directory_path)
36
+ if not directory.exists():
37
+ logger.info(f"Создание директории {directory}")
38
+ directory.mkdir(parents=True, exist_ok=True)
39
+
40
+ # Устанавливаем полные права
41
+ subprocess.run(["chmod", "-R", "777", str(directory)])
42
+ logger.info(f"Права доступа для {directory} установлены")
43
+
44
+ def backup_file(filepath):
45
+ """Создает резервную копию файла"""
46
+ src_path = Path(filepath)
47
+ if not src_path.exists():
48
+ logger.warning(f"Невозможно создать резервную копию: {filepath} не существует")
49
+ return
50
+
51
+ BACKUP_DIR.mkdir(parents=True, exist_ok=True)
52
+ dest_path = BACKUP_DIR / f"{src_path.name}.bak"
53
+
54
+ try:
55
+ shutil.copy2(src_path, dest_path)
56
+ logger.info(f"Резервная копия создана: {dest_path}")
57
+ except Exception as e:
58
+ logger.error(f"Ошибка при создании резервной копии {filepath}: {e}")
59
+
60
  def check_and_create_property_json():
61
  """Проверяет наличие property.json и создает его при необходимости"""
62
+ if not PROPERTY_JSON.exists():
63
+ logger.warning(f"{PROPERTY_JSON} не найден, создаем файл...")
 
 
64
 
65
  property_data = {
66
  "_ten": {}, # Важное поле для TEN формата
 
83
  }
84
 
85
  # Проверяем и создаем директории
86
+ PROPERTY_JSON.parent.mkdir(parents=True, exist_ok=True)
87
 
88
+ # Создаем временный файл и затем перемещаем его
89
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file:
90
+ json.dump(property_data, temp_file, indent=2)
91
+ temp_path = temp_file.name
92
+
93
+ # Копируем временный файл в целевой
94
+ try:
95
+ shutil.copy2(temp_path, PROPERTY_JSON)
96
+ os.chmod(PROPERTY_JSON, 0o666) # Устанавливаем права доступа rw-rw-rw-
97
+ logger.info(f"Файл {PROPERTY_JSON} создан успешно")
98
+ except Exception as e:
99
+ logger.error(f"Ошибка при создании {PROPERTY_JSON}: {e}")
100
+ finally:
101
+ os.unlink(temp_path) # Удаляем временный файл
102
+
103
+ def check_and_create_agent_files():
104
+ """Проверяет наличие всех необходимых файлов агентов и создает их при необходимости"""
105
+
106
+ # Создаем manifest.json если он не существует
107
+ if not MANIFEST_JSON.exists():
108
+ manifest_data = {
109
+ "name": "default",
110
+ "agents": [
111
+ {
112
+ "name": "voice_agent",
113
+ "description": "A simple voice agent"
114
+ },
115
+ {
116
+ "name": "chat_agent",
117
+ "description": "A text chat agent"
118
+ }
119
+ ]
120
+ }
121
+
122
+ with open(MANIFEST_JSON, 'w') as f:
123
+ json.dump(manifest_data, f, indent=2)
124
+ os.chmod(MANIFEST_JSON, 0o666)
125
+ logger.info(f"Файл {MANIFEST_JSON} создан")
126
+
127
+ # Создаем voice_agent.json если он не существует
128
+ if not VOICE_AGENT_JSON.exists():
129
+ voice_agent_data = {
130
+ "nodes": [],
131
+ "edges": [],
132
+ "groups": [],
133
+ "templates": [],
134
+ "root": None
135
+ }
136
+
137
+ with open(VOICE_AGENT_JSON, 'w') as f:
138
+ json.dump(voice_agent_data, f, indent=2)
139
+ os.chmod(VOICE_AGENT_JSON, 0o666)
140
+ logger.info(f"Файл {VOICE_AGENT_JSON} создан")
141
+
142
+ # Создаем chat_agent.json если он не существует
143
+ if not CHAT_AGENT_JSON.exists():
144
+ chat_agent_data = {
145
+ "nodes": [],
146
+ "edges": [],
147
+ "groups": [],
148
+ "templates": [],
149
+ "root": None
150
+ }
151
+
152
+ with open(CHAT_AGENT_JSON, 'w') as f:
153
+ json.dump(chat_agent_data, f, indent=2)
154
+ os.chmod(CHAT_AGENT_JSON, 0o666)
155
+ logger.info(f"Файл {CHAT_AGENT_JSON} создан")
156
 
157
  def check_files():
158
  """Проверяет и выводит информацию о важных файлах"""
159
  files_to_check = [
160
+ PROPERTY_JSON,
161
+ MANIFEST_JSON,
162
+ VOICE_AGENT_JSON,
163
+ CHAT_AGENT_JSON,
164
+ API_BINARY
165
  ]
166
 
167
  logger.info("=== Проверка критических файлов ===")
 
173
  logger.info(f"✅ {file_path} (размер: {size} байт)")
174
 
175
  # Если это JSON файл, выводим его содержимое
176
+ if str(file_path).endswith('.json'):
177
  try:
178
  with open(file_path, 'r') as f:
179
  content = json.load(f)
 
186
  logger.error(f"❌ {file_path} (файл не найден)")
187
 
188
  logger.info("=== Проверка структуры директорий ===")
189
+ logger.info(f"Содержимое {AGENTS_DIR}:")
190
+ subprocess.run(["ls", "-la", str(AGENTS_DIR)])
191
 
192
  logger.info("Проверка прав доступа:")
193
+ subprocess.run(["stat", str(AGENTS_DIR)])
194
+ subprocess.run(["stat", str(PROPERTY_JSON)])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  def test_api():
197
  """Делает запрос к API для получения списка графов"""
 
 
 
198
  logger.info("=== Тестирование API ===")
199
  try:
200
  # Даем серверу время запуститься
 
208
  json_data = json.loads(data)
209
  if isinstance(json_data, list) and len(json_data) > 0:
210
  logger.info(f"API вернул {len(json_data)} графов")
211
+ # Если API вернул пустой список, исправляем это
212
+ if len(json_data) == 0:
213
+ logger.warning("API вернул пустой список графов, исправляем property.json")
214
+ backup_file(PROPERTY_JSON)
215
+ check_and_create_property_json()
216
+ check_and_create_agent_files()
217
+ ensure_directory_permissions(AGENTS_DIR)
218
+ # Перезапускаем API сервер
219
+ logger.info("Перезапускаем API сервер...")
220
+ subprocess.run(["pkill", "-f", str(API_BINARY)])
221
+ time.sleep(1)
222
+ subprocess.Popen([str(API_BINARY)])
223
  else:
224
  logger.warning("API вернул пустой список графов")
225
  except json.JSONDecodeError:
 
232
  def main():
233
  processes = []
234
  try:
 
 
 
 
235
  # Проверяем существование файлов
236
+ if not API_BINARY.exists():
237
+ logger.error(f"API binary не найден: {API_BINARY}")
238
  return 1
239
 
240
+ if not PLAYGROUND_DIR.exists():
241
+ logger.error(f"Playground директория не найдена: {PLAYGROUND_DIR}")
242
  return 1
243
 
244
+ # Создаем директории и устанавливаем права
245
+ ensure_directory_permissions(AGENTS_DIR)
246
+ ensure_directory_permissions(BACKUP_DIR)
247
+
248
  # Проверяем и создаем property.json
249
  check_and_create_property_json()
250
 
251
+ # Проверяем и создаем файлы агентов
252
+ check_and_create_agent_files()
253
 
254
  # Проверка файлов перед запуском
255
  check_files()
256
 
257
  # Запускаем API сервер
258
  logger.info("Запуск TEN-Agent API сервера на порту 8080...")
259
+ api_process = subprocess.Popen([str(API_BINARY)])
260
  processes.append(api_process)
261
 
262
  # Тестируем API
 
276
  os.environ["NEXT_PUBLIC_API_BASE_URL"] = "/api/agents"
277
  os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = "http://localhost:8080"
278
 
279
+ # Запускаем Playground UI
280
  playground_process = subprocess.Popen(
281
  ["pnpm", "dev"],
282
+ cwd=str(PLAYGROUND_DIR),
283
  env=os.environ
284
  )
285
  processes.append(playground_process)