commit 9800c0bfb672e720f2f2a88cb24110ba8cf9fcbc Author: Odysseus Date: Sat Jun 6 12:17:30 2026 +0000 Initial commit diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..1b47839 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,26 @@ +name: Build and Push Docker Image + +on: + push: + branches: [ "main" ] + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Log in to Gitea Registry + uses: docker/login-action@v2 + with: + registry: registry.git.wilkensxl.de + username: ${{ gitea.actor }} + password: ${{ secrets.GITEA_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: registry.git.wilkensxl.de/mrsphay/mobilemanager:latest diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ee2831 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app/ . + +RUN mkdir data + +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..50890c4 --- /dev/null +++ b/app/main.py @@ -0,0 +1,86 @@ +import os +from fastapi import FastAPI, Request, Form, Depends, HTTPException, Response +from fastapi.responses import RedirectResponse +from fastapi.templating import Jinja2Templates +from sqlalchemy import create_engine, Column, Integer, String, Float +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session + +# Database Setup +DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./data/contracts.db") +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +class Contract(Base): + __tablename__ = "contracts" + id = Column(Integer, primary_key=True, index=True) + provider = Column(String) + phone_number = Column(String) + pin = Column(String) + puk = Column(String) + monthly_cost = Column(Float) + +Base.metadata.create_all(bind=engine) + +app = FastAPI() +templates = Jinja2Templates(directory="templates") + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +@app.get("/") +def list_contracts(request: Request, db: Session = Depends(get_db)): + contracts = db.query(Contract).all() + return templates.TemplateResponse("index.html", {"request": request, "contracts": contracts}) + +@app.get("/add") +def add_form(request: Request): + return templates.TemplateResponse("form.html", {"request": request, "action": "add"}) + +@app.post("/add") +def add_contract( + provider: str = Form(...), + phone_number: str = Form(...), + pin: str = Form(...), + puk: str = Form(...), + monthly_cost: float = Form(...), + db: Session = Depends(get_db) +): + new_contract = Contract( + provider=provider, phone_number=phone_number, + pin=pin, puk=puk, monthly_cost=monthly_cost + ) + db.add(new_contract) + db.commit() + return RedirectResponse(url="/", status_code=303) + +@app.get("/edit/{contract_id}") +def edit_form(request: Request, contract_id: int, db: Session = Depends(get_db)): + contract = db.query(Contract).filter(Contract.id == contract_id).first() + if not contract: raise HTTPException(status_code=404) + return templates.TemplateResponse("form.html", {"request": request, "contract": contract, "action": "edit"}) + +@app.post("/edit/{contract_id}") +def edit_contract( + contract_id: int, provider: str = Form(...), phone_number: str = Form(...), + pin: str = Form(...), puk: str = Form(...), monthly_cost: float = Form(...), + db: Session = Depends(get_db) +): + contract = db.query(Contract).filter(Contract.id == contract_id).first() + if not contract: raise HTTPException(status_code=404) + contract.provider, contract.phone_number, contract.pin, contract.puk, contract.monthly_cost = provider, phone_number, pin, puk, monthly_cost + db.commit() + return RedirectResponse(url="/", status_code=303) + +@app.get("/delete/{contract_id}") +def delete_contract(contract_id: int, db: Session = Depends(get_db)): + contract = db.query(Contract).filter(Contract.id == contract_id).first() + if contract: + db.delete(contract) + db.commit() + return RedirectResponse(url="/", status_code=303) diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..091d826 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,29 @@ + + + + + + MobileManager + + + + + +
+ {% block content %}{% endblock %} +
+ + + diff --git a/app/templates/form.html b/app/templates/form.html new file mode 100644 index 0000000..7159929 --- /dev/null +++ b/app/templates/form.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% block content %} +
+

{{ "Bearbeiten" if action == "edit" else "Neuer Vertrag" }}

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+{% endblock %} diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..da9a473 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} +{% block content %} +
+

Meine Verträge

+
+ + + Neuer Vertrag +
+
+ +
+ + + + + + + + + + + + + {% for c in contracts %} + + + + + + + + + {% endfor %} + {% if not contracts %} + + + + {% endif %} + +
ProviderNummerPINPUKKostenAktionen
{{ c.provider }}{{ c.phone_number }}********€{{ "%.2f"|format(c.monthly_cost) }} + Bearbeiten + Löschen +
Keine Verträge vorhanden.
+
+ + +{% endblock %} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ca91bb4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi +uvicorn +sqlalchemy +jinja2 +python-multipart