File size: 33,970 Bytes
90c6b42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
version: "3.6"
services:
  traefik:
    image: "traefik:v2.10.4"
    container_name: "traefik"
    command:
      - "--log.level=ERROR"
      - "--providers.docker=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      # Let's Encrypt Resolver Configuration
      - "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL:-admin@example.com}" # Placeholder for user's email
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.httpchallenge=true"
      - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
      # Global HTTP to HTTPS redirection (optional, can also be per-router)
      # - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      # - "--entrypoints.web.http.redirections.entrypoint.scheme=https"

    labels:
      - traefik.http.middlewares.strip-traefik.stripprefix.prefixes=/v1/traefik
      - traefik.http.routers.traefik.rule=Host(`${HOST_NAME}`) && PathPrefix(`/v1/traefik`) || Host(`localhost`) && PathPrefix(`/v1/traefik`)
      - traefik.http.routers.traefik.entrypoints=web # Traefik dashboard itself can remain on HTTP for simplicity if only accessed locally or via tunnel
      - traefik.http.routers.traefik.middlewares=strip-traefik@docker,traefik-auth@docker # Added auth to traefik dashboard
      - traefik.http.routers.traefik.service=api@internal
      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_USER:-admin}:${TRAEFIK_PASSWORD:-password}" # Renamed middleware for clarity
      # Middleware for HTTP to HTTPS redirection
      - "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"

    ports:
      - "80:80"
      - "443:443"
      - "9090:8080" # Traefik API/Dashboard port (if enabled and needed)
    volumes:
      - ./letsencrypt:/letsencrypt
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami: # Test service, useful for debugging Traefik rules
    image: "traefik/whoami"
    container_name: "whoami"
    expose:
      - 80 # whoami listens on port 80 by default
    labels:
      - "traefik.enable=true"
      # HTTP router for whoami (for testing)
      - "traefik.http.routers.whoami-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/whoami-http`)"
      - "traefik.http.routers.whoami-http.entrypoints=web"
      - "traefik.http.services.whoami.loadbalancer.server.port=80" # whoami default port
      # HTTPS router for whoami (for testing HTTPS setup)
      - "traefik.http.routers.whoami-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/whoami`)"
      - "traefik.http.routers.whoami-https.entrypoints=websecure"
      - "traefik.http.routers.whoami-https.tls=true"
      - "traefik.http.routers.whoami-https.tls.certresolver=le"
      - "traefik.http.routers.whoami-https.service=whoami" # Use the same service definition
  postgres:
    image: postgres:13-alpine
    restart: always
    volumes:
      - ./postgres/data:/var/lib/postgresql/data
      - ./initdb.d:/docker-entrypoint-initdb.d:ro
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secretpgpassword}
      POSTGRES_USERNAME: ${POSTGRES_USERNAME:-postgres}
    healthcheck:
      test:
        ["CMD", "pg_isready", "-U", "${POSTGRES_USERNAME}", "-d", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"
  supertokens:
    image: registry.supertokens.io/supertokens/supertokens-postgresql:6.0
    depends_on:
      postgres:
        condition: service_healthy
    expose:
      - 3567
    environment:
      POSTGRESQL_CONNECTION_URI: ${SUPERTOKENS_POSTGRESQL_CONNECTION_URI:-postgresql://postgres:secretpgpassword@postgres:5432/postgres}
      POSTGRESQL_TABLE_NAMES_PREFIX: Supertokens
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.strip-auth.stripprefix.prefixes=/v1/auth"
      # HTTP router for auth (redirects to HTTPS)
      - "traefik.http.routers.auth-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/auth`)"
      - "traefik.http.routers.auth-http.entrypoints=web"
      - "traefik.http.routers.auth-http.middlewares=strip-auth@docker,https-redirect@docker"
      # HTTPS router for auth
      - "traefik.http.routers.auth-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/auth`)"
      - "traefik.http.routers.auth-https.entrypoints=websecure"
      - "traefik.http.routers.auth-https.tls=true"
      - "traefik.http.routers.auth-https.tls.certresolver=le"
      - "traefik.http.routers.auth-https.middlewares=strip-auth@docker"
      - "traefik.http.routers.auth-https.service=auth-service" # Explicit service name
      - "traefik.http.services.auth-service.loadbalancer.server.port=3567"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3567/hello"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
  postgraphile:
    image: graphile/postgraphile:4 # Using latest V4, V5 is still in beta
    depends_on:
      postgres:
        condition: service_healthy
    restart: always
    ports:
      - "5000:5000" # Default PostGraphile port
    environment:
      DATABASE_URL: postgres://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres
      PGRAPHILE_WATCH_PG: "true" # Enable watch mode for schema changes
      PGRAPHILE_JWT_SECRET: ${POSTGRAPHILE_JWT_SECRET:-secretjwtsecret} # JWT secret for PostGraphile
      PGRAPHILE_JWT_PG_ROLE: public # Default role if not authenticated, or specify a dedicated one
      PGRAPHILE_PG_DEFAULT_ROLE: public # Role to use for unauthenticated requests
      # PostGraphile doesn't have a direct admin secret concept for API access. Access control is via PG roles.
      # For enhanced security, consider using a dedicated read-only role for PostGraphile unless mutations are needed.
      # PGRAPHILE_OWNER_CONNECTION_STRING: postgres://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres # For schema watching and migrations if needed with a more privileged user
      PGRAPHILE_ENABLE_GRAPHIQL: "true"
      # Add any other necessary PostGraphile environment variables here
      # e.g., PGRAPHILE_SCHEMA_NAMES: public,app_public # Specify schemas to expose
      # PGRAPHILE_APPEND_PLUGINS: @graphile-contrib/pg-simplify-inflector,@graphile/pg-pubsub
      # PGRAPHILE_SIMPLE_COLLECTIONS: omit # or 'both' or 'only'
      # PGRAPHILE_ENHANCE_GRAPHIQL: 'true'
      # PGRAPHILE_ALLOW_EXPLAIN: 'true' # For debugging, be cautious in production
    healthcheck:
      test: [
          "CMD-SHELL",
          "curl -f http://localhost:5000/graphql?query={__typename} || exit 1",
        ] # Basic healthcheck query
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  functions:
    build: ../../frontend-nextjs/project/functions
    container_name: "functions"
    depends_on:
      - minio
      - zookeeper
      - kafka1
      - optaplanner
      - postgres
      - postgraphile # Changed from graphql-engine
      - redis # Replaced mongo with redis
    environment:
      BASIC_AUTH: ${BASIC_AUTH:-false}
      FUNCTION_SERVER_URL: ${FUNCTION_SERVER_URL:-http://functions:3000}
      OPENAI_API_KEY: ${OPENAI_API_KEY:-sk-default-openai-key}
      GOOGLE_CLIENT_ID_ANDROID: ${GOOGLE_CLIENT_ID_ANDROID:-default_android_client_id}
      GOOGLE_CLIENT_ID_IOS: ${GOOGLE_CLIENT_ID_IOS:-default_ios_client_id}
      GOOGLE_CLIENT_ID_WEB: ${GOOGLE_CLIENT_ID_WEB:-default_web_client_id}
      GOOGLE_CLIENT_ID_ATOMIC_WEB: ${GOOGLE_CLIENT_ID_ATOMIC_WEB:-}
      GOOGLE_CLIENT_SECRET_ATOMIC_WEB: ${GOOGLE_CLIENT_SECRET_ATOMIC_WEB:-}
      # PostGraphile doesn't use admin secrets, access control is via PG roles
      POSTGRAPHILE_GRAPHQL_URL: http://postgraphile:5000/graphql # New URL for PostGraphile
      # Not applicable for PostGraphile
      GOOGLE_CLIENT_SECRET_WEB: ${GOOGLE_CLIENT_SECRET_WEB:-default_web_secret}
      EMAIL: ${EMAIL:-admin@example.com}
      # Base URL for the Python API service (running in python-agent)
      # NOTE: python-agent service removed - duplicate functionality exists in backend/integrations/
      # PYTHON_API_SERVICE_BASE_URL: ${PYTHON_API_SERVICE_BASE_URL:-http://python-agent:5000}
      DOMAIN: ${DOMAIN:-localhost}
      S3_ENDPOINT: http://minio:8484
      S3_BUCKET: nhost
      S3_ACCESS_KEY: ${STORAGE_ACCESS_KEY:-minioadmin}
      S3_SECRET_KEY: ${STORAGE_SECRET_KEY:-minioadmin}
      KAFKA_USERNAME: ${KAFKA_USERNAME:-kafka-user}
      KAFKA_PASSWORD: ${KAFKA_PASSWORD:-kafka-password}
      HOST_IP: ${HOST_IP:-127.0.0.1}

      ZOOM_PASS_KEY: ${ZOOM_PASS_KEY:-default_zoom_pass_key}
      ZOOM_CLIENT_ID: ${ZOOM_CLIENT_ID:-default_zoom_client_id}
      ZOOM_SALT_FOR_PASS: ${ZOOM_SALT_FOR_PASS:-default_salt}
      ZOOM_IV_FOR_PASS: ${ZOOM_IV_FOR_PASS:-default_iv}
      OPTAPLANNER_URL: http://optaplanner:8081
      OPTAPLANNER_USERNAME: ${OPTAPLANNER_USERNAME:-admin}
      OPTAPLANNER_PASSWORD: ${OPTAPLANNER_PASSWORD:-adminpassword}
      OPTAPLAN_ADMIN_CALLBACK_URL: http://functions:3000/post-process-calendar/onPostOptaCal/on-opta-plan-post-process-calendar-admin
      GOOGLE_CALENDAR_WEBHOOK_URL: https://${HOST_NAME:-localhost}/v1/functions/google-calendar-sync/googleCalendarWebhook/google-calendar-webhook-public
      GOOGLE_PEOPLE_SYNC_ADMIN_URL: https://${HOST_NAME:-localhost}/v1/functions/google-calendar-sync/googlePeopleSync/google-contact-sync-admin
      OPTAPLANNER_DURATION: ${OPTAPLANNER_DURATION:-60}
      OPTAPLANNER_SHORT_DURATION: ${OPTAPLANNER_SHORT_DURATION:-30}
      APP_CLIENT_URL: ${APP_CLIENT_URL:-http://localhost:3000}
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.strip-functions.stripprefix.prefixes=/v1/functions"
      # HTTP router for functions (redirects to HTTPS)
      - "traefik.http.routers.functions-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/functions/`)"
      - "traefik.http.routers.functions-http.entrypoints=web"
      - "traefik.http.routers.functions-http.middlewares=strip-functions@docker,https-redirect@docker"
      # HTTPS router for functions
      - "traefik.http.routers.functions-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/functions/`)"
      - "traefik.http.routers.functions-https.entrypoints=websecure"
      - "traefik.http.routers.functions-https.tls=true"
      - "traefik.http.routers.functions-https.tls.certresolver=le"
      - "traefik.http.routers.functions-https.middlewares=strip-functions@docker"
      - "traefik.http.routers.functions-https.service=functions-service" # Explicit service name
      - "traefik.http.services.functions-service.loadbalancer.server.port=3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"] # Assuming a /healthz endpoint exists or will be added
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s # Give functions a bit longer to start
    restart: always
    expose:
      - 3000
    ports:
      - 3030:3030
    volumes:
      - ../../frontend-nextjs/project/functions:/app
      - functions_node_modules:/app/node_modules
      - ../../frontend-nextjs/project/functions/logs:/app/logs
  minio:
    image: minio/minio:RELEASE.2021-09-24T00-24-24Z
    entrypoint: sh
    command: -c 'mkdir -p /data/nhost && /opt/bin/minio server --console-address :9001 --address :8484 /data'
    environment:
      MINIO_ROOT_USER: ${STORAGE_ACCESS_KEY:-minioadmin}
      MINIO_ROOT_PASSWORD: ${STORAGE_SECRET_KEY:-minioadmin}
    ports:
      - ${MINIO_PORT:-8484}:8484
      - 9001:9001
    volumes:
      - ./data/minio:/data
  mailhog:
    image: mailhog/mailhog
    environment:
      SMTP_HOST: ${AUTH_SMTP_HOST:-mailhog}
      SMTP_PORT: ${AUTH_SMTP_PORT:-1025}
      SMTP_PASS: ${AUTH_SMTP_PASS:-password}
      SMTP_USER: ${AUTH_SMTP_USER:-user}
      SMTP_SECURE: "${AUTH_SMTP_SECURE:-false}"
      SMTP_SENDER: ${AUTH_SMTP_SENDER:-hbp@hbp.com}
    ports:
      - ${AUTH_SMTP_PORT:-1025}:1025
      - 8025:8025
    volumes:
      - ./data/mailhog:/maildir

  zookeeper:
    image: confluentinc/cp-zookeeper:5.4.2
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: "2181"
      ZOOKEEPER_TICK_TIME: "2000"
      KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/server-jaas.conf -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider"
    volumes:
      - ./kafka/kafka/server-jaas.conf:/etc/kafka/server-jaas.conf:ro,z
  kafka1:
    image: confluentinc/cp-kafka:5.4.2
    hostname: kafka1
    container_name: kafka1
    labels:
      - "custom.project=kafkajs"
      - "custom.service=kafka1"
    depends_on:
      - zookeeper
    ports:
      - "29092:29092"
      - "9092:9092"
      - "29093:29093"
      - "9093:9093"
      - "29094:29094"
      - "9094:9094"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: kafka1
      KAFKA_BROKER_ID: "0"
      KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,PLAINTEXT:SASL_PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka1:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
      KAFKA_DEFAULT_REPLICATION_FACTOR: "1"
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
      KAFKA_NUM_PARTITIONS: "1"
      KAFKA_DELETE_TOPIC_ENABLE: "true"
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0"
      KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: "PLAIN"
      KAFKA_SASL_ENABLED_MECHANISMS: "PLAIN,SCRAM-SHA-256,SCRAM-SHA-512"
      KAFKA_OPTS: "-Djava.security.auth.login.config=/opt/kafka/config/server-jaas.conf"
      KAFKA_AUTHORIZER_CLASS_NAME: "kafka.security.auth.SimpleAclAuthorizer"
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
      # suppress verbosity
      # https://github.com/confluentinc/cp-docker-images/blob/master/debian/kafka/include/etc/confluent/docker/log4j.properties.template
      KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
      CONFLUENT_SUPPORT_METRICS_ENABLE: "false"
      CONFLUENT_METRICS_REPORTER_TOPIC_REPLICAS: "1"
    volumes:
      - ./kafka/kafka/server-jaas.conf:/opt/kafka/config/server-jaas.conf:ro,z

  live-meeting-worker:
    build:
      context: ../python-api/live_meeting_worker # Path to the Dockerfile directory
      dockerfile: Dockerfile
    container_name: live-meeting-worker
    depends_on:
      - postgres # If it ever needs to interact with postgres directly
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - NOTION_API_KEY=${NOTION_API_KEY}
      - NOTION_PARENT_PAGE_ID=${NOTION_PARENT_PAGE_ID} # Optional
      - DATABASE_URL=/app/data/live_meeting_tasks.db
    volumes:
      - ./worker_data:/app/data # For SQLite DB persistence
    ports:
      - "8001:8001" # Expose the port FastAPI runs on
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8001/list_audio_devices"] # Basic healthcheck
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.strip-live-meeting-worker.stripprefix.prefixes=/v1/live-meeting-worker"
      # HTTP router (redirects to HTTPS)
      - "traefik.http.routers.live-meeting-worker-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/live-meeting-worker`)"
      - "traefik.http.routers.live-meeting-worker-http.entrypoints=web"
      - "traefik.http.routers.live-meeting-worker-http.middlewares=strip-live-meeting-worker@docker,https-redirect@docker"
      # HTTPS router
      - "traefik.http.routers.live-meeting-worker-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/v1/live-meeting-worker`)"
      - "traefik.http.routers.live-meeting-worker-https.entrypoints=websecure"
      - "traefik.http.routers.live-meeting-worker-https.tls=true"
      - "traefik.http.routers.live-meeting-worker-https.tls.certresolver=le"
      - "traefik.http.routers.live-meeting-worker-https.middlewares=strip-live-meeting-worker@docker"
      - "traefik.http.routers.live-meeting-worker-https.service=live-meeting-worker-service"
      - "traefik.http.services.live-meeting-worker-service.loadbalancer.server.port=8001"

  optaplanner:
    image: atomic-scheduler:latest
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://postgres:5432/postgres
      QUARKUS_DATASOURCE_USERNAME: ${POSTGRES_USERNAME:-postgres}
      QUARKUS_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-secretpgpassword}
      QUARKUS_DATASOURCE_DB-KIND: postgresql
      USERNAME: admin
      PASSWORD: ${API_TOKEN:-secretapitoken}
    ports:
      - 8081:8081
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/q/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s # Quarkus apps can take a moment to start

  app:
    build:
      context: ../../frontend-nextjs
      args:
        POSTGRAPHILE_GRAPHQL_URL: http://postgraphile:5000/graphql # Internal URL for server-side rendering or API routes if needed
        NEXT_PUBLIC_POSTGRAPHILE_GRAPHQL_URL: http://localhost:5000/graphql # URL for client-side

        NEXT_PUBLIC_EVENT_TO_QUEUE_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/schedule-event/publisherScheduleEvent/schedule-event-auth
        NEXT_PUBLIC_EVENT_TO_QUEUE_SHORT_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/schedule-event/publisherScheduleShortEvent/schedule-short-event-auth
        NEXT_PUBLIC_CALENDAR_TO_QUEUE_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/schedule-assist/publisherScheduleMeeting/schedule-meeting-to-queue-auth
        NEXT_PUBLIC_FEATURES_APPLY_TO_EVENTS_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/features-apply/publish-to-features-worker/features-worker-to-queue-auth
        NEXT_PUBLIC_METHOD_TO_SEARCH_INDEX_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/events-search/eventsSearch/events-search-auth
        NEXT_PUBLIC_GOOGLE_CALENDAR_ANDROID_AUTH_URL: https://${HOST_NAME:-localhost}/v1/functions/google-api-auth/googleCalendarAndroidAuth/google-calendar-android-auth
        NEXT_PUBLIC_GOOGLE_CALENDAR_ANDROID_AUTH_REFRESH_URL: https://${HOST_NAME:-localhost}/v1/functions/google-api-auth/googleCalendarAndroidAuthRefresh/google-calendar-android-auth-refresh
        NEXT_PUBLIC_GOOGLE_ATOMIC_WEB_AUTH_REFRESH_URL: https://${HOST_NAME:-localhost}/v1/functions/google-api-auth/googleAtomicWebAuthRefresh/google-atomic-web-auth-refresh
        NEXT_PUBLIC_GOOGLE_CALENDAR_IOS_AUTH_REFRESH_URL: https://${HOST_NAME:-localhost}/v1/functions/google-api-auth/googleCalendarIosAuthRefresh/google-calendar-ios-auth-refresh
        NEXT_PUBLIC_GOOGLE_OAUTH_ATOMIC_WEB_API_START_URL: https://${HOST_NAME:-localhost}/api/google/start-oauth
        NEXT_PUBLIC_GOOGLE_OAUTH_ATOMIC_WEB_REDIRECT_URL: https://${HOST_NAME:-localhost}/api/google/oauth-callback
        GOOGLE_CLIENT_ID_ATOMIC_WEB: ${GOOGLE_CLIENT_ID_ATOMIC_WEB:-default_atomic_web_client_id}
        GOOGLE_CLIENT_SECRET_ATOMIC_WEB: ${GOOGLE_CLIENT_SECRET_ATOMIC_WEB:-default_atomic_web_secret}
        ZOOM_IV_FOR_PASS: ${ZOOM_IV_FOR_PASS:-default_iv}
        ZOOM_SALT_FOR_PASS: ${ZOOM_SALT_FOR_PASS:-default_salt}
        ZOOM_PASS_KEY: ${ZOOM_PASS_KEY:-default_zoom_pass_key}
        NEXT_PUBLIC_EMAIL_MEETING_INFO_TO_HOST_URL: https://${HOST_NAME:-localhost}/v1/functions/email-notification/meeting-info-host/meeting-info-to-host-auth
        NEXT_PUBLIC_EMAIL_MEETING_INVITE_URL: https://${HOST_NAME:-localhost}/v1/functions/email-notification/meeting-invite-email/meeting-invite-auth
        NEXT_PUBLIC_EMAIL_MEETING_CANCEL_URL: https://${HOST_NAME:-localhost}/v1/functions/email-notification/meeting-cancel-email/meeting-cancel-auth
        NEXT_PUBLIC_HANDSHAKE_URL: https://${HOST_NAME:-localhost}/v1/handshake/
        NEXT_PUBLIC_DELETE_ZOOM_CONFERENCE_URL: https://${HOST_NAME:-localhost}/v1/functions/zoom-meeting/delZoomMeet/delete-zoom-meet-auth
        NEXT_PUBLIC_GOOGLE_CALENDAR_SYNC_URL: https://${HOST_NAME:-localhost}/v1/functions/google-calendar-sync/googleCalendarSync/google-calendar-sync-auth
        NEXT_PUBLIC_SELF_GOOGLE_CALENDAR_WATCH_URL: https://${HOST_NAME:-localhost}/v1/functions/google-calendar-sync/googleCalendarWatch/google-calendar-watch-auth
        NEXT_PUBLIC_GOOGLE_OAUTH_START_URL: https://${HOST_NAME:-localhost}/Auth/google/oauth-start
        NEXT_PUBLIC_CHAT_WS_API_URL: ws://localhost:3030
        NEXT_PUBLIC_GOOGLE_PEOPLE_SYNC_URL: https://${HOST_NAME:-localhost}/v1/functions/google-calendar-sync/googlePeopleSync/google-contact-sync-auth
        NEXT_PUBLIC_ADD_DAILY_FEATURES_AUTOPILOT_URL: https://${HOST_NAME:-localhost}/v1/functions/autopilot/addDailyFeatures/add-daily-features-to-event-auth
        NEXT_PUBLIC_DELETE_SCHEDULED_EVENT_URL: https://${HOST_NAME:-localhost}/v1/functions/autopilot/deleteScheduledEvent/delete-scheduled-event-auth
        NEXT_PUBLIC_ZOOM_CREATE_MEETING_URL: https://${HOST_NAME:-localhost}/v1/functions/zoom-meeting/createZoomMeet/create-zoom-meet-auth
        NEXT_PUBLIC_ZOOM_UPDATE_MEETING_URL: https://${HOST_NAME:-localhost}/v1/functions/zoom-meeting/updateZoomMeet/update-zoom-meet-auth
        NEXT_PUBLIC_ZOOM_DELETE_MEETING_URL: https://${HOST_NAME:-localhost}/v1/functions/zoom-meeting/delZoomMeet/delete-zoom-meet-auth
        NEXT_PUBLIC_ZOOM_OAUTH_START_URL: https://${HOST_NAME:-localhost}/zoom/oauth-start
        NEXT_PUBLIC_LIVE_MEETING_WORKER_URL: http://live-meeting-worker:8001
    container_name: app
    depends_on:
      - supertokens
      - live-meeting-worker # Add dependency if app should wait for worker
    labels:
      - "traefik.enable=true"
      # HTTP router for app (redirects to HTTPS)
      - "traefik.http.routers.app-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/`)"
      - "traefik.http.routers.app-http.entrypoints=web"
      - "traefik.http.routers.app-http.middlewares=https-redirect@docker" # Apply global redirect
      # HTTPS router for app
      - "traefik.http.routers.app-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/`)"
      - "traefik.http.routers.app-https.entrypoints=websecure"
      - "traefik.http.routers.app-https.tls=true"
      - "traefik.http.routers.app-https.tls.certresolver=le"
      - "traefik.http.routers.app-https.service=app-service" # Explicit service name
      - "traefik.http.services.app-service.loadbalancer.server.port=3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/"] # Check root path
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 45s # Next.js apps can take a bit to start
    ports:
      - "3000:3000"
    # Add NEXT_PUBLIC_LIVE_MEETING_WORKER_URL to environment or build args
    # If it's passed during build time via build args:
    # build:
    #   context: ../app_build_docker
    #   args:
    #     # ... other args
    #     NEXT_PUBLIC_LIVE_MEETING_WORKER_URL: http://live-meeting-worker:8001
    # If it's passed as a runtime environment variable (more common for URLs):
    environment:
      # ... (ensure all existing environment variables for 'app' are maintained)
      # Example of how it might be structured if environment is already used:
      # EXISTING_ENV_VAR: ${EXISTING_ENV_VAR}
      NEXT_PUBLIC_LIVE_MEETING_WORKER_URL: http://live-meeting-worker:8001
      # PYTHON_API_SERVICE_BASE_URL: ${PYTHON_API_SERVICE_BASE_URL:-http://python-agent:5000} # Removed: python-agent service deprecated
      # The existing 'app' service definition is complex with many build args.
      # Need to be careful to merge this correctly.
      # Based on the structure, build args are used. So I will add it there.

  # python-agent:
  # NOTE: Service removed - python_agent_build_docker directory deleted (2025-02-03)
  # Reason: Duplicate functionality exists in backend/integrations/
  #   - Notion integration: backend/integrations/notion_service.py
  #   - Deepgram integration: backend/integrations/deepgram_service.py
  #   - All other services have real implementations
  # This service contained placeholder Celery tasks that returned "method": "placeholder"
  # To restore this service, uncomment the following and restore python_agent_build_docker directory
  #   python-agent:
  #     build:
  #       context: ../python_agent_build_docker
  #       dockerfile: Dockerfile
  #     container_name: python-agent
  #     depends_on:
  #       - postgres
  #     environment:
  #       - NOTION_API_TOKEN=${NOTION_API_TOKEN:-default_notion_token}
  #       - NOTION_NOTES_DATABASE_ID=${NOTION_NOTES_DATABASE_ID:-default_notes_db_id}
  #       - DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY:-default_deepgram_key}
  #       - NOTION_RESEARCH_PROJECTS_DB_ID=${NOTION_RESEARCH_PROJECTS_DB_ID:-default_research_projects_db_id}
  #       - NOTION_RESEARCH_TASKS_DB_ID=${NOTION_RESEARCH_TASKS_DB_ID:-default_research_tasks_db_id}
  #       - OPENAI_API_KEY=${OPENAI_API_KEY:-sk-default-openai-key}
  #       - LANCEDB_URI=/lancedb_data/atom_core_db
  #       - LANCEDB_TABLE_NAME=${LANCEDB_TABLE_NAME:-meeting_transcripts_embeddings}
  #       - PYTHONUNBUFFERED=1
  #     volumes:
  #       - .:/app/project
  #       - ${LANCEDB_HOST_PATH:-./lance_db_data_shared}:/lancedb_data
  #     tty: true
  #     stdin_open: true

  ingestion-pipeline-service:
    build:
      context: ../python-api/ingestion_pipeline
      dockerfile: Dockerfile
    container_name: ingestion-pipeline-service
    restart: unless-stopped
    depends_on:
      - functions # Example dependency, adjust as needed
    environment:
      - NOTION_API_KEY=${NOTION_API_KEY}
      - NOTION_TRANSCRIPTS_DATABASE_ID=${NOTION_TRANSCRIPTS_DATABASE_ID}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - LANCEDB_URI=/lancedb_data/atom_core_db # Points to the mounted volume + DB name
      - ATOM_USER_ID_FOR_INGESTION=${ATOM_USER_ID_FOR_INGESTION:-default_atom_user_pipeline}
      - PROCESSING_MODE=${PROCESSING_MODE:-incremental}
      - LOG_LEVEL=${LOG_LEVEL:-INFO}
      - PYTHONUNBUFFERED=1
    volumes:
      # Mount the shared LanceDB data volume
      - ${LANCEDB_HOST_PATH:-./lance_db_data_shared}:/lancedb_data
    ports:
      - "8002:8002" # Expose for triggering, can be removed if triggered internally
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -f http://localhost:8002/ingestion-status || exit 1",
        ]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 60s

  redis:
    image: "redis:alpine"
    container_name: redis
    expose:
      - 6379
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 30

  celery-worker:
    build:
      context: ../python_agent_build_docker
      dockerfile: Dockerfile
    container_name: celery-worker
    command: celery -A project.celery_app worker --loglevel=info
    volumes:
      - .:/app/project
      - ${LANCEDB_HOST_PATH:-./lance_db_data_shared}:/lancedb_data
    depends_on:
      - redis
      - postgres
    environment:
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/0
      - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres
      - NOTION_API_TOKEN=${NOTION_API_TOKEN:-default_notion_token}
      - NOTION_NOTES_DATABASE_ID=${NOTION_NOTES_DATABASE_ID:-default_notes_db_id}
      - DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY:-default_deepgram_key}
      - NOTION_RESEARCH_PROJECTS_DB_ID=${NOTION_RESEARCH_PROJECTS_DB_ID:-default_research_projects_db_id}
      - NOTION_RESEARCH_TASKS_DB_ID=${NOTION_RESEARCH_TASKS_DB_ID:-default_research_tasks_db_id}
      - OPENAI_API_KEY=${OPENAI_API_KEY:-sk-default-openai-key}
      - LANCEDB_URI=/lancedb_data/atom_core_db
      - LANCEDB_TABLE_NAME=${LANCEDB_TABLE_NAME:-meeting_transcripts_embeddings}
      - PYTHONUNBUFFERED=1

  # celery-worker:
  # NOTE: Service removed - python_agent_build_docker directory deleted (2025-02-03)
  # Celery background tasks with placeholder implementations (Slack, Email, Asana all returned "method": "placeholder")
  # Real implementations exist in backend/integrations/
  # To restore, uncomment and restore python_agent_build_docker directory
  #   celery-worker:
  #     build:
  #       context: ../python_agent_build_docker
  #       dockerfile: Dockerfile
  #     container_name: celery-worker
  #     command: celery -A project.celery_app worker --loglevel=info
  #     volumes:
  #       - .:/app/project
  #       - ${LANCEDB_HOST_PATH:-./lance_db_data_shared}:/lancedb_data
  #     depends_on:
  #       - redis
  #       - postgres
  #     environment:
  #       - CELERY_BROKER_URL=redis://redis:6379/0
  #       - CELERY_RESULT_BACKEND=redis://redis:6379/0
  #       - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres
  #       - NOTION_API_TOKEN=${NOTION_API_TOKEN:-default_notion_token}
  #       - NOTION_NOTES_DATABASE_ID=${NOTION_NOTES_DATABASE_ID:-default_notes_db_id}
  #       - DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY:-default_deepgram_key}
  #       - NOTION_RESEARCH_PROJECTS_DB_ID=${NOTION_RESEARCH_PROJECTS_DB_ID:-default_research_projects_db_id}
  #       - NOTION_RESEARCH_TASKS_DB_ID=${NOTION_RESEARCH_TASKS_DB_ID:-default_research_tasks_db_id}
  #       - OPENAI_API_KEY=${OPENAI_API_KEY:-sk-default-openai-key}
  #       - LANCEDB_URI=/lancedb_data/atom_core_db
  #       - LANCEDB_TABLE_NAME=${LANCEDB_TABLE_NAME:-meeting_transcripts_embeddings}
  #       - PYTHONUNBUFFERED=1

  # celery-beat:
  # NOTE: Service removed - python_agent_build_docker directory deleted (2025-02-03)
  # Celery beat scheduler for placeholder background tasks
  # To restore, uncomment and restore python_agent_build_docker directory
  #   celery-beat:
  #     build:
  #       context: ../python_agent_build_docker
  #       dockerfile: Dockerfile
  #     container_name: celery-beat
  #     command: celery -A project.celery_app beat --loglevel=info
  #     volumes:
  #       - .:/app/project
  #     depends_on:
  #       - redis
  #       - postgres
  #     environment:
  #       - CELERY_BROKER_URL=redis://redis:6379/0
  #       - CELERY_RESULT_BACKEND=redis://redis:6379/0
  #       - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres

  workflows-api:
    build:
      context: ../python-api/workflows
      dockerfile: Dockerfile
    container_name: workflows-api
    restart: unless-stopped
    depends_on:
      - postgres
      - redis
    environment:
      - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/0
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - NOTION_API_KEY=${NOTION_API_KEY}
    ports:
      - "8003:8003"
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8003/healthz || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.strip-workflows-api.stripprefix.prefixes=/api/workflows"
      - "traefik.http.routers.workflows-api-http.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/api/workflows`)"
      - "traefik.http.routers.workflows-api-http.entrypoints=web"
      - "traefik.http.routers.workflows-api-http.middlewares=strip-workflows-api@docker,https-redirect@docker"
      - "traefik.http.routers.workflows-api-https.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/api/workflows`)"
      - "traefik.http.routers.workflows-api-https.entrypoints=websecure"
      - "traefik.http.routers.workflows-api-https.tls=true"
      - "traefik.http.routers.workflows-api-https.tls.certresolver=le"
      - "traefik.http.routers.workflows-api-https.middlewares=strip-workflows-api@docker"
      - "traefik.http.routers.workflows-api-https.service=workflows-api-service"
      - "traefik.http.services.workflows-api-service.loadbalancer.server.port=8003"

  dropbox-api:
    build:
      context: ../python-api/dropbox_service
      dockerfile: Dockerfile
    container_name: dropbox-api
    restart: unless-stopped
    ports:
      - "5001:5001"
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:5001/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dropbox-api.rule=Host(`${HOST_NAME:-localhost}`) && PathPrefix(`/api/dropbox`)"
      - "traefik.http.routers.dropbox-api.entrypoints=websecure"
      - "traefik.http.routers.dropbox-api.tls=true"
      - "traefik.http.routers.dropbox-api.tls.certresolver=le"
      - "traefik.http.services.dropbox-api.loadbalancer.server.port=5001"

volumes:
  project_node_modules:
  functions_node_modules: