File size: 7,672 Bytes
08aeccf
 
 
 
 
 
 
 
 
a36ef66
8081ea9
 
9e83698
 
 
08aeccf
e7a94f7
 
08aeccf
8081ea9
08aeccf
 
 
 
 
 
 
 
 
 
e56bcc7
 
8081ea9
9e83698
08aeccf
8081ea9
 
5230c81
08aeccf
9e83698
c0987db
bcf12b3
 
 
 
 
 
 
 
 
 
 
 
 
c0987db
9e83698
bcf12b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e83698
 
 
 
bcf12b3
9e83698
 
51c8261
5230c81
 
 
 
9e83698
c0987db
 
 
 
 
 
 
 
 
 
08aeccf
 
 
 
 
a36ef66
e56bcc7
08aeccf
 
 
 
 
 
 
 
 
 
e7a94f7
08aeccf
e7a94f7
08aeccf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f308308
 
 
 
9e83698
 
e7a94f7
 
 
 
 
08aeccf
 
 
e7a94f7
08aeccf
e7a94f7
08aeccf
e56bcc7
08aeccf
 
a36ef66
 
 
e56bcc7
a36ef66
 
e56bcc7
 
 
 
 
 
 
 
a36ef66
08aeccf
e56bcc7
 
 
f0c6f87
e56bcc7
08aeccf
 
 
a36ef66
e56bcc7
 
 
 
f308308
08aeccf
 
 
f308308
e56bcc7
 
08aeccf
 
 
 
e56bcc7
 
 
 
 
08aeccf
 
 
 
 
 
9e83698
a36ef66
9e83698
a36ef66
 
 
08aeccf
 
9e83698
 
 
bcf12b3
 
 
 
5230c81
 
08aeccf
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# Base image
FROM python:3.12-slim-bookworm AS base

# Set shared environment variables
ENV POETRY_VERSION=1.8.4 \
    POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_CREATE=true \
    POETRY_VIRTUALENVS_IN_PROJECT=true \
    POETRY_CACHE_DIR=/tmp/poetry_cache \
    PYTHONDONTWRITEBYTECODE=1 \
    LANG=en_US.UTF-8 \
    LANGUAGE=en_US:en \
    LC_ALL=en_US.UTF-8 \
    PORT=7860 \
    NODE_ENV=production

# Create user first (HF Spaces requirement)
RUN useradd -m -u 1000 user

# Install system dependencies and set up locales
RUN apt-get update && apt-get install -y \
    curl \
    git \
    gcc \
    python3-dev \
    libgmp-dev \
    libmpfr-dev \
    libmpc-dev \
    nodejs \
    npm \
    postgresql \
    postgresql-contrib \
    locales \
    nginx \
    && rm -rf /var/lib/apt/lists/* \
    && pip install --no-cache-dir "poetry==${POETRY_VERSION}" \
    && sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
    && locale-gen

# Configure nginx
RUN rm /etc/nginx/sites-enabled/default || true
COPY <<EOF /etc/nginx/nginx.conf
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    upstream frontend {
        server 127.0.0.1:3000;
    }

    upstream backend {
        server 127.0.0.1:5001;
    }

    server {
        listen 7860;
        server_name _;
        client_max_body_size 100M;
        
        location / {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            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;
            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;
        }

        location /api {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            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;
            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;
        }

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
    }
}
EOF

RUN mkdir -p /var/log/nginx && \
    chown -R user:user /var/log/nginx && \
    touch /run/nginx.pid && \
    chown -R user:user /run/nginx.pid && \
    chown -R user:user /var/lib/nginx && \
    chmod -R 755 /var/lib/nginx && \
    chmod -R 755 /etc/nginx && \
    chmod -R 777 /run

# Set up PostgreSQL directories with standard locations
RUN mkdir -p /var/run/postgresql /var/lib/postgresql/data /var/log/postgresql && \
    chown -R user:user /var/run/postgresql /var/lib/postgresql/data /var/log/postgresql && \
    chmod 700 /var/lib/postgresql/data

# Create application directories
RUN mkdir -p /app/api /app/web /data/storage && \
    chown -R user:user /app /data && \
    chmod 777 /data /app

# Switch to user for remaining operations
USER user

# Set environment for user
ENV HOME=/home/user \
    PATH=/usr/lib/postgresql/15/bin:/home/user/.local/bin:$PATH \
    PGDATA=/var/lib/postgresql/data

# Pull official images
FROM langgenius/dify-web:latest AS web
FROM langgenius/dify-api:latest AS api

# Final stage
FROM base

# Set up directory structure
WORKDIR /app
RUN mkdir -p api web /data/storage

# Copy from official images
COPY --from=web --chown=user:user /app/web /app/web/
COPY --from=api --chown=user:user /app/api /app/api/

# Install API dependencies using Poetry
WORKDIR /app/api
COPY --from=api --chown=user /app/api/pyproject.toml /app/api/poetry.lock /app/api/poetry.toml ./
RUN poetry install --no-root --no-dev

# Create symlink for persistent storage
RUN ln -s /data/storage /app/api/storage

# Set environment variables
ENV FLASK_APP=app.py \
    EDITION=SELF_HOSTED \
    DEPLOY_ENV=PRODUCTION \
    MODE=api \
    LOG_LEVEL=INFO \
    DEBUG=false \
    FLASK_DEBUG=false \
    SECRET_KEY=sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U \
    CONSOLE_API_URL=https://${SPACE_ID}.hf.space \
    CONSOLE_WEB_URL=https://${SPACE_ID}.hf.space \
    SERVICE_API_URL=https://${SPACE_ID}.hf.space \
    APP_WEB_URL=https://${SPACE_ID}.hf.space \
    DIFY_PORT=5001 \
    DIFY_BIND_ADDRESS=127.0.0.1 \
    DB_USERNAME=user \
    DB_PASSWORD=difyai123456 \
    DB_HOST=localhost \
    DB_PORT=5432 \
    DB_DATABASE=dify \
    PYTHONPATH=/app/api \
    STORAGE_PATH=/data/storage

EXPOSE 7860

# Create startup script
RUN echo '#!/bin/bash\n\
set -e\n\
echo "===== Application Startup at $(date "+%Y-%m-%d %H:%M:%S") ====="\n\
\n\
# Initialize PostgreSQL database if not already initialized\n\
if [ ! -f "$PGDATA/PG_VERSION" ]; then\n\
    echo "Initializing PostgreSQL database..."\n\
    initdb --username=user --pwfile=<(echo "$DB_PASSWORD") --auth=md5 --encoding=UTF8\n\
    \n\
    # Configure PostgreSQL\n\
    echo "local all all trust" > "$PGDATA/pg_hba.conf"\n\
    echo "host all all 127.0.0.1/32 md5" >> "$PGDATA/pg_hba.conf"\n\
    echo "host all all ::1/128 md5" >> "$PGDATA/pg_hba.conf"\n\
    echo "host all all 0.0.0.0/0 md5" >> "$PGDATA/pg_hba.conf"\n\
    \n\
    echo "listen_addresses = '\''*'\''" >> "$PGDATA/postgresql.conf"\n\
    echo "max_connections = 100" >> "$PGDATA/postgresql.conf"\n\
    echo "shared_buffers = 128MB" >> "$PGDATA/postgresql.conf"\n\
fi\n\
\n\
# Start PostgreSQL with detailed logging\n\
echo "Starting PostgreSQL server..."\n\
pg_ctl start -D "$PGDATA" -l /var/log/postgresql/postgresql.log -o "-c logging_collector=on -c log_directory='\''/var/log/postgresql'\'' -c log_filename='\''postgresql-%Y-%m-%d_%H%M%S.log'\'' -c log_statement='\''all'\''" -w\n\
\n\
# Wait for PostgreSQL to start and show logs if there are issues\n\
max_tries=30\n\
count=0\n\
echo "Checking database connection..."\n\
until pg_isready -h localhost -p 5432; do\n\
    if [ $count -eq 0 ]; then\n\
        echo "PostgreSQL logs:"\n\
        tail -n 50 /var/log/postgresql/postgresql.log\n\
    fi\n\
    echo "Waiting for database connection... (${count}/${max_tries})"\n\
    sleep 2\n\
    count=$((count+1))\n\
    if [ $count -gt $max_tries ]; then\n\
        echo "Failed to connect to database after ${max_tries} attempts"\n\
        echo "Last 100 lines of PostgreSQL logs:"\n\
        tail -n 100 /var/log/postgresql/postgresql.log\n\
        exit 1\n\
    fi\n\
done\n\
\n\
# Create database if it doesn'\''t exist\n\
if ! psql -lqt | cut -d \| -f 1 | grep -qw dify; then\n\
    echo "Creating database dify..."\n\
    createdb -U user dify\n\
fi\n\
\n\
echo "Database connection successful"\n\
\n\
# Start application services\n\
cd /app/api && poetry run python -m flask db upgrade\n\
\n\
# Start API server\n\
cd /app/api && poetry run python -m gunicorn app:app \\\n\
    --bind ${DIFY_BIND_ADDRESS:-127.0.0.1}:${DIFY_PORT:-5001} \\\n\
    --worker-class gevent \\\n\
    --workers 1 \\\n\
    --timeout 300 \\\n\
    --preload &\n\
\n\
# Start frontend server\n\
cd /app/web && PORT=3000 node server.js &\n\
\n\
# Wait for services to be ready\n\
echo "Waiting for services to be ready..."\n\
sleep 5\n\
\n\
# Start nginx\n\
nginx -g "daemon off;" &\n\
\n\
wait' > /app/entrypoint.sh && \
chmod +x /app/entrypoint.sh

WORKDIR /app

CMD ["./entrypoint.sh"]