Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
livindra 2026-04-28 14:03:31 +07:00
parent b615610e1d
commit 0f52c3dc03
8 changed files with 68 additions and 14 deletions

View File

@ -5,5 +5,15 @@ DB_USER=root
DB_PASSWORD= DB_PASSWORD=
DB_NAME=deteksi_pmk DB_NAME=deteksi_pmk
# Railway / hosted MySQL support
# Railway biasanya menyediakan variabel ini otomatis saat plugin database dipasang.
MYSQLHOST=
MYSQLPORT=
MYSQLUSER=
MYSQLPASSWORD=
MYSQLDATABASE=
DATABASE_URL=
# Flask Configuration # Flask Configuration
FLASK_SECRET=your-secret-key-here FLASK_SECRET=your-secret-key-here
FLASK_DEBUG=0

1
Procfile Normal file
View File

@ -0,0 +1 @@
web: gunicorn app:app --bind 0.0.0.0:$PORT

View File

@ -22,6 +22,21 @@ python app.py
Buka browser ke: **http://localhost:5000** Buka browser ke: **http://localhost:5000**
## Deploy ke Railway
Repository ini sudah disiapkan untuk Railway dengan entrypoint Flask di `app.py`.
1. Hubungkan repository ini ke Railway.
2. Tambahkan layanan database MySQL di Railway jika ingin penyimpanan riwayat berjalan.
3. Set environment variable berikut di Railway:
- `FLASK_SECRET`
- `DATABASE_URL` atau `MYSQLHOST`, `MYSQLPORT`, `MYSQLUSER`, `MYSQLPASSWORD`, `MYSQLDATABASE`
4. Railway akan menjalankan `gunicorn app:app --bind 0.0.0.0:$PORT`.
Catatan:
- `opencv-python-headless` dipakai supaya build Railway tidak bergantung pada library GUI.
- Jika model belum ada di folder `models/`, jalankan training dulu sebelum deploy.
## Langkah 3: Upload Gambar & Lihat Riwayat ## Langkah 3: Upload Gambar & Lihat Riwayat
1. **Upload**: Klik "Upload Gambar" → Pilih file → Upload 1. **Upload**: Klik "Upload Gambar" → Pilih file → Upload
@ -45,6 +60,8 @@ DB_HOST=localhost
DB_USER=root DB_USER=root
DB_PASSWORD= DB_PASSWORD=
DB_NAME=deteksi_pmk DB_NAME=deteksi_pmk
Untuk Railway, lebih aman pakai variabel environment yang disediakan oleh plugin database atau `DATABASE_URL`.
``` ```
## 🔗 Useful Routes ## 🔗 Useful Routes

4
app.py
View File

@ -911,4 +911,6 @@ def export_excel():
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True) port = int(os.environ.get('PORT', 5000))
debug = os.environ.get('FLASK_DEBUG', '0') == '1'
app.run(host='0.0.0.0', port=port, debug=debug)

@ -1 +0,0 @@
Subproject commit ce2e5771dd78cd0ade90b677c15668361743e345

11
railway.json Normal file
View File

@ -0,0 +1,11 @@
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "gunicorn app:app --bind 0.0.0.0:$PORT",
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}

View File

@ -1,4 +1,4 @@
opencv-python==4.9.0.80 opencv-python-headless==4.9.0.80
numpy<2 numpy<2
pandas>=2.1.0 pandas>=2.1.0
scikit-learn>=1.3.2 scikit-learn>=1.3.2
@ -9,4 +9,5 @@ joblib>=1.3.2
Flask>=2.3.3 Flask>=2.3.3
SQLAlchemy>=2.0.0 SQLAlchemy>=2.0.0
PyMySQL>=1.1.0 PyMySQL>=1.1.0
python-dotenv>=1.0.0 python-dotenv>=1.0.0
gunicorn>=21.2.0

View File

@ -8,6 +8,7 @@ import json
import datetime import datetime
from collections import OrderedDict from collections import OrderedDict
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Text, Boolean, ForeignKey, Table, inspect, text from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Text, Boolean, ForeignKey, Table, inspect, text
from sqlalchemy.engine import make_url
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
@ -16,18 +17,30 @@ from sqlalchemy.exc import SQLAlchemyError
import pytz import pytz
TZ_INDONESIA = pytz.timezone('Asia/Jakarta') TZ_INDONESIA = pytz.timezone('Asia/Jakarta')
# Database configuration from environment variables DATABASE_URL = os.environ.get('DATABASE_URL')
DB_HOST = os.environ.get('DB_HOST', 'localhost')
DB_PORT = os.environ.get('DB_PORT', '3306')
DB_USER = os.environ.get('DB_USER', 'root') def _build_database_uri():
DB_PASSWORD = os.environ.get('DB_PASSWORD', '') """Build a SQLAlchemy database URI from Railway or local environment variables."""
DB_NAME = os.environ.get('DB_NAME', 'deteksi_pmk') if DATABASE_URL:
url = make_url(DATABASE_URL)
if url.drivername == 'mysql':
url = url.set(drivername='mysql+pymysql')
return str(url)
db_host = os.environ.get('DB_HOST') or os.environ.get('MYSQLHOST', 'localhost')
db_port = os.environ.get('DB_PORT') or os.environ.get('MYSQLPORT', '3306')
db_user = os.environ.get('DB_USER') or os.environ.get('MYSQLUSER', 'root')
db_password = os.environ.get('DB_PASSWORD') or os.environ.get('MYSQLPASSWORD', '')
db_name = os.environ.get('DB_NAME') or os.environ.get('MYSQLDATABASE', 'deteksi_pmk')
if db_password:
return f"mysql+pymysql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
return f"mysql+pymysql://{db_user}@{db_host}:{db_port}/{db_name}"
# Create database URI # Create database URI
if DB_PASSWORD: DATABASE_URI = _build_database_uri()
DATABASE_URI = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
else:
DATABASE_URI = f"mysql+pymysql://{DB_USER}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
# Create engine # Create engine
engine = create_engine(DATABASE_URI, echo=False, pool_pre_ping=True) engine = create_engine(DATABASE_URI, echo=False, pool_pre_ping=True)