Skip to content

Commit 89db37e

Browse files
authored
Update README.md
1 parent dd908b9 commit 89db37e

1 file changed

Lines changed: 197 additions & 16 deletions

File tree

README.md

Lines changed: 197 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Streamlit Launcher
22

3-
<img src="https://github.com/DwiDevelopes/gambar/raw/main/Desain%20tanpa%20judul%20(8).jpg" width="100%" height="100%">
3+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/launcher.jpg" width="100%" height="100%">
44

55
[![PyPI version](https://badge.fury.io/py/streamlit-launcher.svg)](https://badge.fury.io/py/streamlit-launcher)
66
[![Downloads](https://pepy.tech/badge/streamlit-launcher/week)](https://pepy.tech/project/streamlit-launcher)
@@ -14,19 +14,19 @@
1414

1515
- **PyPI Package**: [https://pypi.org/project/streamlit-launcher/](https://pypi.org/project/streamlit-launcher/)
1616
- **Demo Online**: [https://stremlit-launcher.streamlit.app/](https://stremlit-launcher.streamlit.app/)
17-
- **GitHub Repository**: [https://github.com/royhtml/streamlit-launcher](https://github.com/royhtml/streamlit-launcher)
17+
- **GitHub Repository**: [https://github.com/DwiDevelopes/Launcher](https://github.com/DwiDevelopes/Launcher)
1818
- **Documentation**: [https://streamlit-launcher-docs.readthedocs.io/](https://streamlit-launcher-docs.readthedocs.io/)
1919

2020
## 📊 Statistik Penggunaan
2121

2222
| Metric | Value |
2323
|--------|-------|
24-
| Total Downloads | 15,000+ |
24+
| Total Downloads | 10,000+ |
2525
| Monthly Downloads | 2,500+ |
2626
| Weekly Downloads | 600+ |
2727
| Python Version Support | 3.7+ |
2828
| Streamlit Version | 1.28+ |
29-
| Test Coverage | 85%+ |
29+
| Test Coverage | 92%+ |
3030
| Last Update | September 2024 |
3131

3232
## 📖 Overview
@@ -42,10 +42,41 @@
4242
- **Researchers** - Eksperimen dan presentasi hasil penelitian
4343
- **Educators** - Materi pembelajaran interaktif
4444

45-
# Update 3.8.7 Install Streamlit Launcher Make Termux ☕
45+
# Streamlit Launcher V4 Update
46+
47+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/launcher.jpg" width="100%" height="100%">
48+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/1.png" width="100%" />
49+
<p align="center">
50+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/2.png" width="48%" />
51+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/3.png" width="48%" />
52+
</p>
53+
<p align="center">
54+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/4.png" width="48%" />
55+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/5.png" width="48%" />
56+
</p>
57+
<p align="center">
58+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/6.png" width="48%" />
59+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/tas/7.png" width="48%" />
60+
</p>
61+
62+
## Update V4 Grafik Saham
63+
64+
<img src="https://github.com/DwiDevelopes/gambar/raw/main/Screenshot%202025-11-09%20001502.png" width="100%" height="100%">
65+
66+
67+
1. Menambahkan grafik saham
68+
2. Menambahkan fitur pencarian
69+
3. Menambahkan fitur pengaturan
70+
4. Menambahkan fitur pengaturan pengguna
71+
5. Vitur Saham Grafik
72+
6. Chart Saham Lengkap
73+
7. OLDC
74+
75+
# Update V4 Install Streamlit Launcher Make Termux ☕
4676

4777
<img src="https://github.com/DwiDevelopes/gambar/raw/main/Desain%20tanpa%20judul%20(10).jpg" width="100%" height="100%">
4878

79+
4980
# 🌱 Pantun untuk Anak Programming
5081
Baik, saatnya puisi kecil untuk pejuang logika digital.
5182
Pantun bukan algoritma, tapi ritmenya juga perlu rapi supaya compiler kehidupan tidak error karna logika.
@@ -319,7 +350,7 @@ Bayangkan ini sebagai menu spell:
319350
- 📈 Remove Background
320351
- 📈 dll
321352

322-
# Update 3.8.7 Anlisis And Doctor Analisis and Install Streamlit Launcher Make Termux ☕
353+
# Update V4 Anlisis And Doctor Analisis and Install Streamlit Launcher Make Termux ☕
323354

324355
## vitur Update Api Key And Api Server
325356

@@ -342,39 +373,189 @@ import pandas as pd
342373
import sqlite3
343374
from datetime import datetime
344375
import os
376+
import logging
377+
from typing import Optional, Tuple, Any
378+
379+
# Setup logging
380+
logging.basicConfig(level=logging.INFO)
381+
logger = logging.getLogger(__name__)
345382

346383
app = Flask(__name__)
384+
app.config['JSON_SORT_KEYS'] = False
347385

348-
def validate_api_key(api_key):
386+
class DatabaseManager:
387+
"""Class untuk mengelola koneksi database"""
388+
389+
@staticmethod
390+
def get_connection(db_path: str) -> sqlite3.Connection:
391+
"""Membuat koneksi database dengan error handling"""
392+
try:
393+
conn = sqlite3.connect(db_path)
394+
conn.row_factory = sqlite3.Row # Mengembalikan hasil sebagai dictionary
395+
return conn
396+
except sqlite3.Error as e:
397+
logger.error(f"Database connection error: {e}")
398+
raise
399+
400+
def validate_api_key(api_key: str) -> bool:
401+
"""
402+
Validasi API key dari database
403+
404+
Args:
405+
api_key: String API key untuk divalidasi
406+
407+
Returns:
408+
Boolean indicating validity of API key
409+
"""
410+
if not api_key:
411+
return False
412+
349413
try:
350-
conn = sqlite3.connect('api_keys.db')
414+
conn = DatabaseManager.get_connection('api_keys.db')
351415
c = conn.cursor()
352-
c.execute("SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1", (api_key,))
416+
417+
c.execute("""
418+
SELECT * FROM api_keys
419+
WHERE api_key = ? AND is_active = 1
420+
""", (api_key,))
421+
353422
result = c.fetchone()
354423
conn.close()
424+
355425
return result is not None
426+
427+
except sqlite3.Error as e:
428+
logger.error(f"Database error during API key validation: {e}")
429+
return False
356430
except Exception as e:
357-
print(f"Validation error: {e}")
431+
logger.error(f"Unexpected error during API key validation: {e}")
358432
return False
359433

434+
def get_api_key() -> Optional[str]:
435+
"""
436+
Mendapatkan API key dari header atau query parameters
437+
438+
Returns:
439+
API key string atau None jika tidak ditemukan
440+
"""
441+
# Cek di header terlebih dahulu
442+
api_key = request.headers.get('X-API-Key')
443+
444+
# Jika tidak ada di header, cek di query parameters
445+
if not api_key:
446+
api_key = request.args.get('api_key')
447+
448+
return api_key
449+
450+
@app.before_request
451+
def log_request_info():
452+
"""Middleware untuk logging setiap request"""
453+
logger.info(f"Request: {request.method} {request.path} - IP: {request.remote_addr}")
454+
455+
@app.after_request
456+
def after_request(response):
457+
"""Middleware untuk menambahkan header security"""
458+
response.headers.add('X-Content-Type-Options', 'nosniff')
459+
response.headers.add('X-Frame-Options', 'DENY')
460+
response.headers.add('X-XSS-Protection', '1; mode=block')
461+
return response
462+
360463
@app.route('/api/v1/health', methods=['GET'])
361464
def health_check():
362-
return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})
465+
"""Endpoint untuk health check API"""
466+
try:
467+
# Test database connection
468+
conn = DatabaseManager.get_connection('api_keys.db')
469+
conn.close()
470+
471+
return jsonify({
472+
'status': 'healthy',
473+
'timestamp': datetime.now().isoformat(),
474+
'version': '1.0.0'
475+
})
476+
except Exception as e:
477+
logger.error(f"Health check failed: {e}")
478+
return jsonify({
479+
'status': 'unhealthy',
480+
'error': str(e),
481+
'timestamp': datetime.now().isoformat()
482+
}), 503
363483

364484
@app.route('/api/v1/statistics', methods=['GET'])
365485
def get_statistics():
366-
api_key = request.args.get('api_key')
486+
"""Endpoint untuk mendapatkan statistics"""
487+
api_key = get_api_key()
488+
367489
if not api_key or not validate_api_key(api_key):
368-
return jsonify({'error': 'Invalid or inactive API key'}), 401
490+
logger.warning(f"Invalid API key attempt from IP: {request.remote_addr}")
491+
return jsonify({
492+
'error': 'Invalid or inactive API key',
493+
'code': 'UNAUTHORIZED'
494+
}), 401
369495

370496
try:
371497
# Your statistics logic here
372-
return jsonify({'status': 'success', 'data': 'statistics'})
498+
# Contoh: membaca data dari database atau file
499+
statistics_data = {
500+
'total_users': 1000,
501+
'active_users': 750,
502+
'requests_today': 15000,
503+
'average_response_time': '45ms'
504+
}
505+
506+
logger.info(f"Statistics request successful for API key: {api_key[:8]}...")
507+
508+
return jsonify({
509+
'status': 'success',
510+
'timestamp': datetime.now().isoformat(),
511+
'data': statistics_data
512+
})
513+
373514
except Exception as e:
374-
return jsonify({'error': str(e)}), 500
515+
logger.error(f"Error in statistics endpoint: {e}")
516+
return jsonify({
517+
'error': 'Internal server error',
518+
'code': 'INTERNAL_ERROR'
519+
}), 500
520+
521+
@app.errorhandler(404)
522+
def not_found(error):
523+
"""Handler untuk 404 Not Found"""
524+
return jsonify({
525+
'error': 'Endpoint not found',
526+
'code': 'NOT_FOUND'
527+
}), 404
528+
529+
@app.errorhandler(405)
530+
def method_not_allowed(error):
531+
"""Handler untuk 405 Method Not Allowed"""
532+
return jsonify({
533+
'error': 'Method not allowed',
534+
'code': 'METHOD_NOT_ALLOWED'
535+
}), 405
536+
537+
@app.errorhandler(500)
538+
def internal_server_error(error):
539+
"""Handler untuk 500 Internal Server Error"""
540+
logger.error(f"Internal server error: {error}")
541+
return jsonify({
542+
'error': 'Internal server error',
543+
'code': 'INTERNAL_ERROR'
544+
}), 500
375545

376546
if __name__ == '__main__':
377-
app.run(host='0.0.0.0', port=5000, debug=True)
547+
# Cek apakah database exists
548+
if not os.path.exists('api_keys.db'):
549+
logger.warning("Database file 'api_keys.db' not found. Please create it first.")
550+
551+
# Run aplikasi
552+
debug_mode = os.getenv('FLASK_DEBUG', 'False').lower() == 'true'
553+
554+
app.run(
555+
host=os.getenv('FLASK_HOST', '0.0.0.0'),
556+
port=int(os.getenv('FLASK_PORT', 5000)),
557+
debug=debug_mode
558+
)
378559
```
379560

380561
## vitur Update Scatterplot Visualisasi Interval

0 commit comments

Comments
 (0)