From 0dfedcafab9331466f2a799a762311f9290392fc Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:15:47 +0300 Subject: [PATCH 01/28] Create .env.example for environment variable setup Added a sample environment variables file with configuration details for database, cache, data feed, AI engine, API performance, security, and monitoring. --- .env.example | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..629d4e4 --- /dev/null +++ b/.env.example @@ -0,0 +1,66 @@ +# .env.example — نموذج متغيرات البيئة (لا يحتوي أسراراً) +# ضع القيم الحقيقية في Render -> Environment (أو ملف .env محلياً) + +# ---------- Database & Cache ---------- +DATABASE_URL= +DB_POOL_SIZE=10 +DB_TIMEOUT=30 + +REDIS_URL= +REDIS_TOKEN= +REDIS_CACHE_TTL=5 + +# ---------- Data Feed ---------- +# Dukascopy (أو استخدم بروكسي dukascopy-api-websocket) +DUKASCOPY_USER= +DUKASCOPY_PASS= +DUKASCOPY_WS_URL=wss://datafeed.dukascopy.com/ + +# Fallback providers +TWELVEDATA_API_KEY= +OANDA_API_KEY= +OANDA_ACCOUNT_ID= + +# Symbols and timeframes +SYMBOLS=XAUUSD,EURUSD,GBPUSD,USDJPY,USDCHF,AUDUSD,USDCAD,US30,NAS100,GER40,USOIL +DEFAULT_SYMBOL=XAUUSD +DEFAULT_TIMEFRAME=1m +AVAILABLE_TIMEFRAMES=1m,3m,5m,15m,30m,1h,4h,1d + +# ---------- MT5/Execution (optional) ---------- +BROKER_ENVIRONMENT=paper +MT5_SERVER_IP= +MT5_SERVER_PORT= +MT5_ACCOUNT_LOGIN= +MT5_ACCOUNT_PASSWORD= +MAX_RISK_PER_TRADE_PERCENT=2.0 + +# ---------- AI Engine ---------- +MODEL_ENABLED=true +MODEL_TYPE=xgboost +MODEL_PATH=models/market_model.pkl +USE_GPU_ACCELERATION=false +CONFIDENCE_THRESHOLD=0.65 +PREDICTION_INTERVAL_SECONDS=60 + +# ---------- API & Performance ---------- +API_HOST=0.0.0.0 +API_PORT=8000 +API_WORKERS=2 +TICK_BATCH_SIZE=500 +BATCH_INTERVAL=1.0 +MAX_QUEUE=20000 +WORKER_THREADS=4 + +# ---------- Security ---------- +ENV=production +CORS_ORIGINS=* # مؤقتًا أثناء التطوير؛ غيّره للـ production لاحقًا +API_KEY= +JWT_SECRET= +ENCRYPTION_KEY= +RATE_LIMIT_REQUESTS=120 +RATE_LIMIT_WINDOW_SECONDS=60 + +# ---------- Monitoring ---------- +LOG_LEVEL=INFO +SENTRY_DSN= From 8634cb33303545890c57d1163fc04f005c7265e7 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:18:02 +0300 Subject: [PATCH 02/28] Update .gitignore to streamline ignored files Removed various entries related to IDEs and build artifacts while adding environment and log file entries. --- .gitignore | 74 +++++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 6b63e9f..f0fcb1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,29 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ -.mvn - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -# batch files -*.bat - -# svn -.svn - -# mvn -mvnw -mvnw.cmd - - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ +# Environment files +.env +.env.local +.env.production +.env.development + +# Python +__pycache__/ +*.pyc +.pytest_cache/ + +# Node / frontend +node_modules/ +.next/ + +# Data / ML artifacts (do not upload large datasets or model weights) +data/*.csv +data/*.parquet +data/*.json +data/ticks/ + +models/*.pkl +models/*.h5 +models/*.pt +models/*.onnx + +# logs +*.log +logs/ From 80cfc762a01ee79b06a74aba9b50770f865a4086 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:23:22 +0300 Subject: [PATCH 03/28] Add requirements.txt with project dependencies --- requirements.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c37d0d5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +asyncpg==0.27.0 +websockets==10.4 +aiohttp==3.8.4 +aioredis==2.0.1 +python-dotenv==1.0.0 +ujson==5.7.0 +numpy==1.26.4 +pandas==2.2.2 +fastapi==0.100.0 +uvicorn==0.22.0 +xgboost==1.8.4 +joblib==1.3.2 +ta==0.12.1 +python-jose==3.3.0 +psycopg2-binary==2.9.10 From 7412845da562026df24c1cb05b173362db178e85 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:24:03 +0300 Subject: [PATCH 04/28] Add market_stream.py for WebSocket data handling --- data_feed/market_stream.py | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 data_feed/market_stream.py diff --git a/data_feed/market_stream.py b/data_feed/market_stream.py new file mode 100644 index 0000000..3c03555 --- /dev/null +++ b/data_feed/market_stream.py @@ -0,0 +1,102 @@ +# data_feed/market_stream.py +import os, asyncio, json, traceback, time +from datetime import datetime, timezone +import asyncpg +import websockets +from dotenv import load_dotenv + +load_dotenv() + +PROXY_WS = os.getenv("PROXY_WS_URL") or os.getenv("DUKASCOPY_WS_URL") +DB_DSN = os.getenv("DATABASE_URL") +BATCH_SIZE = int(os.getenv("TICK_BATCH_SIZE", "500")) +BATCH_INTERVAL = float(os.getenv("BATCH_INTERVAL", "1.0")) +MAX_QUEUE = int(os.getenv("MAX_QUEUE", "20000")) + +queue = asyncio.Queue(maxsize=MAX_QUEUE) + +def now_iso(): + return datetime.now(timezone.utc).isoformat() + +def normalize(msg_text): + try: + d = json.loads(msg_text) + except Exception: + return None + # try many shapes + symbol = d.get("symbol") or d.get("instrument") or d.get("pair") or d.get("s") + if symbol and "/" in symbol: symbol = symbol.replace("/", "") + bid = d.get("bid") or d.get("b") or d.get("price") + ask = d.get("ask") or d.get("a") or (d.get("price") and d.get("price")) + vol = d.get("volume") or d.get("v") or d.get("size") or 0 + ts = d.get("timestamp") or d.get("ts") or d.get("time") or None + try: + if ts: + ts = int(ts) + ts_iso = datetime.fromtimestamp(ts/1000, tz=timezone.utc).isoformat() if ts>10**12 else datetime.fromtimestamp(ts, tz=timezone.utc).isoformat() + else: + ts_iso = now_iso() + except: + ts_iso = now_iso() + if not symbol or (bid is None and ask is None): + return None + if bid is None: bid = ask + if ask is None: ask = bid + try: + bid = float(bid); ask = float(ask); vol = float(vol) + except: + pass + return (symbol, ts_iso, bid, ask, vol) + +async def pg_pool(): + return await asyncpg.create_pool(dsn=DB_DSN, min_size=1, max_size=10) + +async def writer_worker(pool): + buffer = [] + last_flush = time.time() + while True: + item = await queue.get() + buffer.append(item) + if len(buffer) >= BATCH_SIZE or (time.time()-last_flush)>=BATCH_INTERVAL: + records = [(r[0], r[1], r[2], r[3], r[4]) for r in buffer] + buffer = [] + last_flush = time.time() + async with pool.acquire() as conn: + try: + await conn.copy_records_to_table('ticks', records=records, columns=['symbol','timestamp','bid','ask','volume']) + except Exception: + for rec in records: + try: + await conn.execute("INSERT INTO ticks(symbol,timestamp,bid,ask,volume) VALUES($1,$2,$3,$4,$5)", *rec) + except Exception as e: + print("insert err:", e) + queue.task_done() + +async def ws_consumer(): + pool = await pg_pool() + workers = [asyncio.create_task(writer_worker(pool)) for _ in range(2)] + backoff = 1 + while True: + try: + async with websockets.connect(PROXY_WS, max_size=None) as ws: + print("connected to", PROXY_WS) + # optional subscribe message + sub = os.getenv("PROXY_SUBSCRIBE_MESSAGE") + if sub: + await ws.send(sub) + async for msg in ws: + n = normalize(msg) + if n: + try: + await queue.put(n) + except asyncio.QueueFull: + _ = queue.get_nowait() # drop oldest + await queue.put(n) + except Exception as e: + print("ws err:", e) + traceback.print_exc() + await asyncio.sleep(backoff) + backoff = min(backoff*2, 60) + +if __name__ == "__main__": + asyncio.run(ws_consumer()) From 0a383046641cf2627757d96c8a1b8d23a153032e Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:24:51 +0300 Subject: [PATCH 05/28] Add CandleBuilder class for processing market data --- processing/candle_builder.py | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 processing/candle_builder.py diff --git a/processing/candle_builder.py b/processing/candle_builder.py new file mode 100644 index 0000000..d12abef --- /dev/null +++ b/processing/candle_builder.py @@ -0,0 +1,45 @@ +# processing/candle_builder.py +from datetime import datetime, timezone +from collections import defaultdict +import asyncpg +import os + +DB_DSN = os.getenv("DATABASE_URL") +TIMEFRAME_SECONDS = { + "1m":60, "3m":180, "5m":300, "15m":900, "30m":1800, "1h":3600, "4h":14400, "1d":86400 +} + +class CandleBuilder: + def __init__(self, pool): + self.pool = pool + self.candles = defaultdict(dict) # key=(symbol,tf) -> candle dict + + def bucket(self, ts, tf_s): + epoch = int(ts.timestamp()) + return epoch - (epoch % tf_s) + + async def process_tick(self, symbol, ts_iso, price, vol): + ts = datetime.fromisoformat(ts_iso) + for tf, s in TIMEFRAME_SECONDS.items(): + key = (symbol, tf) + b = self.bucket(ts, s) + c = self.candles.get(key) + if not c or c['bucket']!=b: + if c: + await self.save_candle(symbol, tf, c) + self.candles[key] = {'bucket':b, 'open':price,'high':price,'low':price,'close':price,'vol':vol} + else: + c['high']=max(c['high'], price) + c['low']=min(c['low'], price) + c['close']=price + c['vol'] += vol + + async def save_candle(self, symbol, tf, c): + ts_iso = datetime.fromtimestamp(c['bucket'], tz=timezone.utc).isoformat() + async with self.pool.acquire() as conn: + await conn.execute(""" + INSERT INTO candles(symbol, timeframe, timestamp, open, high, low, close, volume) + VALUES($1,$2,$3,$4,$5,$6,$7,$8) + ON CONFLICT (symbol,timeframe,timestamp) DO UPDATE + SET open=EXCLUDED.open, high=EXCLUDED.high, low=EXCLUDED.low, close=EXCLUDED.close, volume=EXCLUDED.volume + """, symbol, tf, ts_iso, c['open'], c['high'], c['low'], c['close'], c['vol']) From e24cbd1b994217ba722a8d3f26257703460464d6 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:25:44 +0300 Subject: [PATCH 06/28] Create FastAPI application with WebSocket and CORS Implement FastAPI server with WebSocket support and CORS. --- api/main.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 api/main.py diff --git a/api/main.py b/api/main.py new file mode 100644 index 0000000..307ed4b --- /dev/null +++ b/api/main.py @@ -0,0 +1,56 @@ +# api/main.py +import os, asyncio, json +from fastapi import FastAPI, WebSocket, Depends, HTTPException +import asyncpg +from fastapi.middleware.cors import CORSMiddleware +from dotenv import load_dotenv + +load_dotenv() +app = FastAPI(title="Market API") + +origins = os.getenv("CORS_ORIGINS","*") +if origins == "*": + allow_origins = ["*"] +else: + allow_origins = [o.strip() for o in origins.split(",")] + +app.add_middleware( + CORSMiddleware, + allow_origins=allow_origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +DB_DSN = os.getenv("DATABASE_URL") + +@app.on_event("startup") +async def startup(): + app.state.pool = await asyncpg.create_pool(dsn=DB_DSN, min_size=1, max_size=10) + app.state.subscribers = set() + +@app.on_event("shutdown") +async def shutdown(): + await app.state.pool.close() + +@app.get("/api/v1/candles/{symbol}/{timeframe}") +async def get_candles(symbol: str, timeframe: str, limit: int = 200): + pool = app.state.pool + rows = await pool.fetch( + "SELECT timestamp, open, high, low, close, volume FROM candles WHERE symbol=$1 AND timeframe=$2 ORDER BY timestamp DESC LIMIT $3", + symbol, timeframe, limit + ) + return [dict(r) for r in rows] + +@app.websocket("/ws/market") +async def ws_market(ws: WebSocket): + await ws.accept() + app.state.subscribers.add(ws) + try: + while True: + await asyncio.sleep(60) # placeholder; actual pushes from collector via Redis/pubsub + except Exception: + pass + finally: + app.state.subscribers.remove(ws) + await ws.close() From 5bea862d0548eaf20f600cbdcb89d842d22c7c37 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:27:20 +0300 Subject: [PATCH 07/28] Add prediction model loading and prediction functions --- prediction/predictor.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 prediction/predictor.py diff --git a/prediction/predictor.py b/prediction/predictor.py new file mode 100644 index 0000000..20252d3 --- /dev/null +++ b/prediction/predictor.py @@ -0,0 +1,19 @@ +# prediction/predictor.py +import os, joblib, numpy as np +from typing import Dict + +MODEL_PATH = os.getenv("MODEL_PATH","models/market_model.pkl") +_model = None + +def load_model(): + global _model + if _model is None: + _model = joblib.load(MODEL_PATH) + return _model + +def predict(features: Dict): + model = load_model() + X = np.array([features[k] for k in sorted(features.keys())]).reshape(1,-1) + proba = model.predict_proba(X) if hasattr(model, "predict_proba") else None + pred = model.predict(X) + return {"pred":float(pred[0]), "proba": proba.tolist() if proba is not None else None} From 550b72252f72e85a88fc6876a54a52b4a5b9ea0e Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 02:32:01 +0300 Subject: [PATCH 08/28] Switch base image to Python and configure app --- Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6f15179..a2afd63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ -FROM openjdk:17-jdk-alpine -EXPOSE 7080 -EXPOSE 7081 -ARG JAR_FILE=target/dukascopy-api-websocket-1.0.war -ADD ${JAR_FILE} dukascopy-api-websocket.war -ENTRYPOINT ["java","-jar","dukascopy-api-websocket.war","--dukascopy.credential-username=username", "--dukascopy.credential-password=password"] \ No newline at end of file +FROM python:3.10-slim +WORKDIR /app +COPY requirements.txt /app/ +RUN pip install --no-cache-dir -r requirements.txt +COPY . /app +ENV PYTHONUNBUFFERED=1 +CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] From 18a85b9c68896d41d2b93c60f545ff9729ff92db Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 03:17:41 +0300 Subject: [PATCH 09/28] Update Dockerfile From 0d1be4fbad152f57213c1ac9d27d1150941990ac Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 04:46:30 +0300 Subject: [PATCH 10/28] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c37d0d5..ba94bcc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ asyncpg==0.27.0 websockets==10.4 aiohttp==3.8.4 -aioredis==2.0.1 +redis==5.0.1 python-dotenv==1.0.0 ujson==5.7.0 numpy==1.26.4 From 524ccf458b60071defd7ab037c4b3ee8d8425bca Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:10:37 +0300 Subject: [PATCH 11/28] Switch to Maven build and Java runtime in Dockerfile --- Dockerfile | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index a2afd63..0640edf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,18 @@ -FROM python:3.10-slim +FROM maven:3.9-eclipse-temurin-17 AS build + +WORKDIR /build +COPY . . + +RUN mvn clean package -DskipTests + + +FROM openjdk:17-jdk-slim + WORKDIR /app -COPY requirements.txt /app/ -RUN pip install --no-cache-dir -r requirements.txt -COPY . /app -ENV PYTHONUNBUFFERED=1 -CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] + +COPY --from=build /build/target/dukascopy-api-websocket-1.0.war app.war + +EXPOSE 7080 +EXPOSE 7081 + +CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] From 15fc6bb8a6dae00da8c2430214d34b92342742d9 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:18:00 +0300 Subject: [PATCH 12/28] Create docker-image.yml --- .github/workflows/docker-image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..3f53646 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) From 666719c797f3c97439c06295f6c0c198d93c89e7 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:18:22 +0300 Subject: [PATCH 13/28] Create maven-publish.yml --- .github/workflows/maven-publish.yml | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/maven-publish.yml diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml new file mode 100644 index 0000000..64b848b --- /dev/null +++ b/.github/workflows/maven-publish.yml @@ -0,0 +1,34 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: Maven Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml + env: + GITHUB_TOKEN: ${{ github.token }} From d3c463963d3e1f045c5bb4b5889ef5c840d74330 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:18:48 +0300 Subject: [PATCH 14/28] Create python-package-conda.yml --- .github/workflows/python-package-conda.yml | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/python-package-conda.yml diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml new file mode 100644 index 0000000..f358604 --- /dev/null +++ b/.github/workflows/python-package-conda.yml @@ -0,0 +1,34 @@ +name: Python Package using Conda + +on: [push] + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file environment.yml --name base + - name: Lint with flake8 + run: | + conda install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + conda install pytest + pytest From 356d5965b67407c6e5c0e4dd5963764e42ceec25 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:32:42 +0300 Subject: [PATCH 15/28] Delete .github/workflows directory --- .github/workflows/docker-image.yml | 18 ------------ .github/workflows/maven-publish.yml | 34 ---------------------- .github/workflows/python-package-conda.yml | 34 ---------------------- 3 files changed, 86 deletions(-) delete mode 100644 .github/workflows/docker-image.yml delete mode 100644 .github/workflows/maven-publish.yml delete mode 100644 .github/workflows/python-package-conda.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 3f53646..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml deleted file mode 100644 index 64b848b..0000000 --- a/.github/workflows/maven-publish.yml +++ /dev/null @@ -1,34 +0,0 @@ -# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created -# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path - -name: Maven Package - -on: - release: - types: [created] - -jobs: - build: - - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 - with: - java-version: '11' - distribution: 'temurin' - server-id: github # Value of the distributionManagement/repository/id field of the pom.xml - settings-path: ${{ github.workspace }} # location for the settings.xml file - - - name: Build with Maven - run: mvn -B package --file pom.xml - - - name: Publish to GitHub Packages Apache Maven - run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml - env: - GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml deleted file mode 100644 index f358604..0000000 --- a/.github/workflows/python-package-conda.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Python Package using Conda - -on: [push] - -jobs: - build-linux: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: '3.10' - - name: Add conda to system path - run: | - # $CONDA is an environment variable pointing to the root of the miniconda directory - echo $CONDA/bin >> $GITHUB_PATH - - name: Install dependencies - run: | - conda env update --file environment.yml --name base - - name: Lint with flake8 - run: | - conda install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - conda install pytest - pytest From aeebbd641971ddc010f86d53cdac8fb9b7209bf7 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 05:33:42 +0300 Subject: [PATCH 16/28] Change base image to eclipse-temurin:17-jre --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0640edf..c41c957 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY . . RUN mvn clean package -DskipTests -FROM openjdk:17-jdk-slim +FROM eclipse-temurin:17-jre WORKDIR /app From bbda80901aaae34c7aad3731b970ee568657dd6a Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:16:01 +0300 Subject: [PATCH 17/28] Update Dockerfile for build and runtime configurations --- Dockerfile | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index c41c957..8dfd3e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,12 @@ -FROM maven:3.9-eclipse-temurin-17 AS build - -WORKDIR /build +# مرحلة البناء +FROM maven:3.9.6-eclipse-temurin-17 AS build +WORKDIR /app COPY . . - RUN mvn clean package -DskipTests - +# مرحلة التشغيل FROM eclipse-temurin:17-jre - WORKDIR /app - -COPY --from=build /build/target/dukascopy-api-websocket-1.0.war app.war - -EXPOSE 7080 -EXPOSE 7081 - -CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] +# استبدل target/<اسم-ملف-jar>.jar باسم الملف النهائي بشكل صحيح! +COPY --from=build /app/target/*.jar app.jar +CMD ["java", "-jar", "app.jar"] From f1d897b26439c5439824e69876611e0afccef743 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:23:49 +0300 Subject: [PATCH 18/28] Update Dockerfile --- Dockerfile | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8dfd3e6..2f2e7ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,29 @@ -# مرحلة البناء -FROM maven:3.9.6-eclipse-temurin-17 AS build -WORKDIR /app -COPY . . +# -------------------------- +# مرحلة البناء (Maven) +# -------------------------- +FROM maven:3.9.9-eclipse-temurin-17 AS builder +WORKDIR /build + +# نسخ ملفات المشروع +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src + +# بناء المشروع بدون اختبارات RUN mvn clean package -DskipTests +# -------------------------- # مرحلة التشغيل -FROM eclipse-temurin:17-jre +# -------------------------- +FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# استبدل target/<اسم-ملف-jar>.jar باسم الملف النهائي بشكل صحيح! -COPY --from=build /app/target/*.jar app.jar -CMD ["java", "-jar", "app.jar"] + +# نسخ الملف الناتج من مرحلة البناء +COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war + +EXPOSE 7080 +EXPOSE 7081 + +# أمر التشغيل النهائي +CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] From 26652b87ced706de5827bb570a30c996789360b5 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:30:58 +0300 Subject: [PATCH 19/28] Update Dockerfile --- Dockerfile | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2f2e7ff..4a64cc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,20 @@ -# -------------------------- -# مرحلة البناء (Maven) -# -------------------------- +# مرحلة البناء FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ ملفات المشروع COPY pom.xml . -RUN mvn dependency:go-offline -B - COPY src ./src -# بناء المشروع بدون اختبارات +# بناء المشروع بدون اختبار RUN mvn clean package -DskipTests -# -------------------------- # مرحلة التشغيل -# -------------------------- FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ الملف الناتج من مرحلة البناء COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war EXPOSE 7080 EXPOSE 7081 -# أمر التشغيل النهائي CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] From dce4199caff71e976fd65f652f4b95bc0b21e5c3 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:33:33 +0300 Subject: [PATCH 20/28] Modify Dockerfile to include WAR file and CMD Updated Dockerfile to copy the final WAR file and set CMD. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4a64cc6..93faee3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,13 +5,13 @@ WORKDIR /build COPY pom.xml . COPY src ./src -# بناء المشروع بدون اختبار RUN mvn clean package -DskipTests # مرحلة التشغيل FROM eclipse-temurin:17-jre-jammy WORKDIR /app +# نسخ ملف WAR النهائي COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war EXPOSE 7080 From 83978c48ab96391f91135c4863bdac798f2409d1 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:52:29 +0300 Subject: [PATCH 21/28] Update Dockerfile for build and run stages --- Dockerfile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93faee3..c60f2bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,25 @@ -# مرحلة البناء +# ---------- مرحلة البناء ---------- FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build +# نسخ ملفات المشروع COPY pom.xml . -COPY src ./src +COPY java ./src/java +COPY resources ./src/resources +# بناء المشروع بدون تشغيل الاختبارات RUN mvn clean package -DskipTests -# مرحلة التشغيل +# ---------- مرحلة التشغيل ---------- FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ ملف WAR النهائي +# نسخ ملف WAR النهائي من مرحلة البناء COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war +# فتح البورتات المطلوبة EXPOSE 7080 EXPOSE 7081 +# بدء التطبيق CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] From 795cbbe655b888f4c0ade494603101fe966d49c7 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 06:57:04 +0300 Subject: [PATCH 22/28] Update Dockerfile for improved build and runtime commands --- Dockerfile | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index c60f2bf..ebdac00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,18 @@ -# ---------- مرحلة البناء ---------- +# ============================ +# مرحلة البناء (Build) +# ============================ FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ ملفات المشروع -COPY pom.xml . -COPY java ./src/java -COPY resources ./src/resources +# نسخ كل ملفات المشروع +COPY . . -# بناء المشروع بدون تشغيل الاختبارات +# تحميل التبعيات وبناء المشروع بدون تشغيل الاختبارات RUN mvn clean package -DskipTests -# ---------- مرحلة التشغيل ---------- +# ============================ +# مرحلة التشغيل (Runtime) +# ============================ FROM eclipse-temurin:17-jre-jammy WORKDIR /app @@ -21,5 +23,5 @@ COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war EXPOSE 7080 EXPOSE 7081 -# بدء التطبيق -CMD ["java","-jar","app.war","--dukascopy.credential-username=DEMO2dwYGQ","--dukascopy.credential-password=dwYGQ"] +# أمر تشغيل المشروع مع متغيرات Dukascopy من ملف .env +CMD ["sh","-c","java -jar app.war --dukascopy.credential-username=$DUKASCOPY_USER --dukascopy.credential-password=$DUKASCOPY_PASS"] From 2cea4fa6b68c2252d4c104884db8cb41f86db38b Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:03:44 +0300 Subject: [PATCH 23/28] Update Dockerfile comments and formatting --- Dockerfile | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index ebdac00..79d7ce3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,23 @@ -# ============================ -# مرحلة البناء (Build) -# ============================ +# ---------- مرحلة البناء ---------- FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ كل ملفات المشروع +# نسخ جميع ملفات المشروع COPY . . # تحميل التبعيات وبناء المشروع بدون تشغيل الاختبارات RUN mvn clean package -DskipTests -# ============================ -# مرحلة التشغيل (Runtime) -# ============================ +# ---------- مرحلة التشغيل ---------- FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ ملف WAR النهائي من مرحلة البناء +# نسخ ملف WAR النهائي COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war -# فتح البورتات المطلوبة +# فتح البورتات EXPOSE 7080 EXPOSE 7081 -# أمر تشغيل المشروع مع متغيرات Dukascopy من ملف .env +# بدء التطبيق مع متغيرات البيئة من .env CMD ["sh","-c","java -jar app.war --dukascopy.credential-username=$DUKASCOPY_USER --dukascopy.credential-password=$DUKASCOPY_PASS"] From 9e42f3d50b9d714a41ac6f78ab76c5377f943d11 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:21:39 +0300 Subject: [PATCH 24/28] Update Dockerfile --- Dockerfile | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 79d7ce3..537f828 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,37 @@ -# ---------- مرحلة البناء ---------- +# ============================ +# مرحلة البناء (Build) +# ============================ FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ جميع ملفات المشروع -COPY . . +# نسخ ملفات المشروع (pom.xml والكود المصدري) +COPY pom.xml . +COPY src ./src # تحميل التبعيات وبناء المشروع بدون تشغيل الاختبارات RUN mvn clean package -DskipTests -# ---------- مرحلة التشغيل ---------- +# ============================ +# مرحلة التشغيل (Runtime) +# ============================ FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ ملف WAR النهائي -COPY --from=builder /build/target/dukascopy-api-websocket-1.0.war app.war +# نسخ ملف WAR الناتج من مرحلة البناء +COPY --from=builder /build/target/*.war app.war -# فتح البورتات -EXPOSE 7080 -EXPOSE 7081 +# تعريض المنفذ (سيتم تجاوزه بواسطة Render عبر متغير PORT) +EXPOSE 8080 -# بدء التطبيق مع متغيرات البيئة من .env -CMD ["sh","-c","java -jar app.war --dukascopy.credential-username=$DUKASCOPY_USER --dukascopy.credential-password=$DUKASCOPY_PASS"] +# متغيرات البيئة الافتراضية (يتم استبدالها بقيم من Render) +ENV DUKE_USERNAME=${DUKASCOPY_USER} \ + DUKE_PASSWORD=${DUKASCOPY_PASS} \ + DB_URL=${DATABASE_URL} \ + SERVER_PORT=${PORT:-8080} + +# نقطة الدخول: تشغيل التطبيق مع تمرير المتغيرات +CMD java -jar app.war \ + --server.port=$SERVER_PORT \ + --dukascopy.username=$DUKE_USERNAME \ + --dukascopy.password=$DUKE_PASSWORD \ + --spring.datasource.url=$DB_URL From 0d35907f1a0365f4f70fdfcfdb542ef4e3580c2e Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:26:48 +0300 Subject: [PATCH 25/28] Refactor Dockerfile for improved caching and flexibility --- Dockerfile | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 537f828..0c8c0da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,11 +4,14 @@ FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ ملفات المشروع (pom.xml والكود المصدري) +# نسخ ملفات المشروع (pom.xml أولاً لتحسين التخزين المؤقت للتبعيات) COPY pom.xml . +RUN mvn dependency:go-offline -B + +# نسخ باقي الكود المصدري COPY src ./src -# تحميل التبعيات وبناء المشروع بدون تشغيل الاختبارات +# بناء التطبيق (إنشاء ملف WAR) RUN mvn clean package -DskipTests # ============================ @@ -17,17 +20,17 @@ RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ ملف WAR الناتج من مرحلة البناء +# نسخ ملف WAR الناتج من مرحلة البناء (باستخدام wildcard لمرونة الاسم) COPY --from=builder /build/target/*.war app.war -# تعريض المنفذ (سيتم تجاوزه بواسطة Render عبر متغير PORT) +# تعريف المنافذ التي قد يستخدمها التطبيق (سيتم تجاوزها بواسطة Render) EXPOSE 8080 -# متغيرات البيئة الافتراضية (يتم استبدالها بقيم من Render) -ENV DUKE_USERNAME=${DUKASCOPY_USER} \ +# متغيرات البيئة الافتراضية (يتم استبدالها بقيم من خدمة Render) +ENV SERVER_PORT=${PORT:-8080} \ + DUKE_USERNAME=${DUKASCOPY_USER} \ DUKE_PASSWORD=${DUKASCOPY_PASS} \ - DB_URL=${DATABASE_URL} \ - SERVER_PORT=${PORT:-8080} + DB_URL=${DATABASE_URL} # نقطة الدخول: تشغيل التطبيق مع تمرير المتغيرات CMD java -jar app.war \ From ac0c2892f96f5f4648c980683d00925b37d9dea4 Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:37:54 +0300 Subject: [PATCH 26/28] Refactor Dockerfile for improved build process --- Dockerfile | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0c8c0da..d5cb238 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,14 +4,11 @@ FROM maven:3.9.9-eclipse-temurin-17 AS builder WORKDIR /build -# نسخ ملفات المشروع (pom.xml أولاً لتحسين التخزين المؤقت للتبعيات) +# نسخ ملفات المشروع (pom.xml أولاً) COPY pom.xml . -RUN mvn dependency:go-offline -B - -# نسخ باقي الكود المصدري COPY src ./src -# بناء التطبيق (إنشاء ملف WAR) +# بناء التطبيق مباشرة (دون go-offline) RUN mvn clean package -DskipTests # ============================ @@ -20,19 +17,14 @@ RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre-jammy WORKDIR /app -# نسخ ملف WAR الناتج من مرحلة البناء (باستخدام wildcard لمرونة الاسم) COPY --from=builder /build/target/*.war app.war - -# تعريف المنافذ التي قد يستخدمها التطبيق (سيتم تجاوزها بواسطة Render) EXPOSE 8080 -# متغيرات البيئة الافتراضية (يتم استبدالها بقيم من خدمة Render) ENV SERVER_PORT=${PORT:-8080} \ DUKE_USERNAME=${DUKASCOPY_USER} \ DUKE_PASSWORD=${DUKASCOPY_PASS} \ DB_URL=${DATABASE_URL} -# نقطة الدخول: تشغيل التطبيق مع تمرير المتغيرات CMD java -jar app.war \ --server.port=$SERVER_PORT \ --dukascopy.username=$DUKE_USERNAME \ From a90bad98c14f4e3aa67db014c3fc45bb81033eae Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:47:20 +0300 Subject: [PATCH 27/28] Update Dockerfile to use Java 11 and optimize build --- Dockerfile | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index d5cb238..4159c89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,40 @@ # ============================ -# مرحلة البناء (Build) +# مرحلة البناء (Build) باستخدام Java 11 # ============================ -FROM maven:3.9.9-eclipse-temurin-17 AS builder +FROM maven:3.8-openjdk-11 AS builder WORKDIR /build -# نسخ ملفات المشروع (pom.xml أولاً) +# نسخ ملف POM أولاً للاستفادة من التخزين المؤقت للتبعيات COPY pom.xml . +RUN mvn dependency:go-offline -B || true + +# نسخ باقي الكود المصدري COPY src ./src -# بناء التطبيق مباشرة (دون go-offline) +# بناء التطبيق (إنشاء ملف WAR) RUN mvn clean package -DskipTests # ============================ -# مرحلة التشغيل (Runtime) +# مرحلة التشغيل (Runtime) باستخدام Java 11 # ============================ -FROM eclipse-temurin:17-jre-jammy +FROM openjdk:11-jre-slim WORKDIR /app +# نسخ ملف WAR الناتج من مرحلة البناء COPY --from=builder /build/target/*.war app.war + +# تعريف المنفذ (سيتم تجاوزه بواسطة Render عبر متغير PORT) EXPOSE 8080 +# متغيرات البيئة الافتراضية (يتم استبدالها بقيم من خدمة Render) ENV SERVER_PORT=${PORT:-8080} \ DUKE_USERNAME=${DUKASCOPY_USER} \ DUKE_PASSWORD=${DUKASCOPY_PASS} \ DB_URL=${DATABASE_URL} +# نقطة الدخول: تشغيل التطبيق مع تمرير المتغيرات CMD java -jar app.war \ --server.port=$SERVER_PORT \ - --dukascopy.username=$DUKE_USERNAME \ - --dukascopy.password=$DUKE_PASSWORD \ + --dukascopy.credential-username=$DUKE_USERNAME \ + --dukascopy.credential-password=$DUKE_PASSWORD \ --spring.datasource.url=$DB_URL From d26c8fe8b5e5044a675af1255c23d7cc64f687cb Mon Sep 17 00:00:00 2001 From: Akram_alhaddad Date: Mon, 16 Mar 2026 07:50:42 +0300 Subject: [PATCH 28/28] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4159c89..c68c8cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,9 +15,9 @@ COPY src ./src RUN mvn clean package -DskipTests # ============================ -# مرحلة التشغيل (Runtime) باستخدام Java 11 +# مرحلة التشغيل (Runtime) باستخدام Eclipse Temurin (Java 11) # ============================ -FROM openjdk:11-jre-slim +FROM eclipse-temurin:11-jre WORKDIR /app # نسخ ملف WAR الناتج من مرحلة البناء