diff --git a/.gitignore b/.gitignore index 0f0c103..4fa2b83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -data venv src/static/uploads +data +src/lovedb.db +src/__pycache__ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..55488c7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.14.3-alpine3.23 + +WORKDIR /app + +COPY requirements.txt . + +RUN apk add gpg + +RUN pip install --no-cache-dir -r requirements.txt + +COPY src/ ./src/ + +EXPOSE 5000 + +ENV FLASK_APP=src/main.py +ENV FLASK_RUN_HOST=0.0.0.0 +ENV FLASK_ENV=production + +CMD ["flask", "run"] diff --git a/README.md b/README.md index b638102..a2f8e8c 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,14 @@ # Dating-Website ## Description Minimal dating website made in python. -It uses flask to render the HTML, saves the data in a MySQL database and also features an authentication method using PGP where the database stores the user's PGP key's fingerprint and uses that to encrypt a message only you decrypt, but successfully decrypting the message you athenticate to your account. +It uses flask to render the HTML, saves the data in a database and also features an authentication method using PGP where the database stores the user's PGP key's fingerprint and uses that to encrypt a message only you decrypt, but successfully decrypting the message you athenticate to your account. It's also supposed to be very easy to use, currently its still in development but the vision is that you can be enganged on the website right from the start featuring a very powerfull search page and not needing an account to use the website. This website also does not use JavaScript making it easy to run on any browser. -## TODO -- making the website responsive -- adding search features -- likes and dislikes -- cool css -- a grid with all of the users on the index page (where the search will also be) -- security audits -- maybe more stuff later... - ## Contributing If you have suggestions, find bugs, or want to provide code, just open an issue before submiting a PR. I will probably not accept a PR unless I see that it's actually somewhat important, exceptions can be made, but its kinda goofy to write the code before submiting an issue. ## Running the program -#### Docker/Podman -`docker compose up -d` / `podman-compose up -d` - ### python enviornment `python -m venv venv` diff --git a/docker-compose.yml b/docker-compose.yml index 7885b4a..55b906c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,14 @@ - services: - db: - image: mariadb:latest - container_name: lovedb - restart: always - environment: - MARIADB_ROOT_PASSWORD: love - MARIADB_DATABASE: lovedb - MARIADB_USER: love - MARIADB_PASSWORD: love + web: + build: . + container_name: dating ports: - - "3309:3306" + - "5000:5000" volumes: - - ./data:/var/lib/mysql + - ./src:/app/src + - ./data:/app/data + environment: + - FLASK_APP=src/main.py + - FLASK_RUN_HOST=0.0.0.0 + - FLASK_ENV=development + restart: unless-stopped diff --git a/plan.md b/plan.md deleted file mode 100644 index 1b47670..0000000 --- a/plan.md +++ /dev/null @@ -1,93 +0,0 @@ -# User System Plan - -## 1. Database Schema (`User` model) - -- **Identity & Security** - - `username` (unique, required) - - `pgp` (unique, required) - - `is_verified` (boolean) - -- **Contacts** - - `xmpp` (unique, required) - - `email` (unique, optional) - - `phone` (unique, optional) - -- **Personal Info** - - `firstname` (required) - - `lastname` (required) - - `sex` (`male` / `female`, required) - - `date_of_birth` (required) - - `race` (optional) - -- **Profile & Media** - - `profile_picture` (required) - - `pictures` (optional array) - -- **Location** - - `country` (required) - - `city` (optional) - -- **Physical Attributes** - - `height` (optional float) - - `weight` (optional int) - -- **Preferences** - - `prefered_age_range` (optional) - - `likes` (optional array) - - `dislikes` (optional array) - ---- - -## 2. Registration - -1. **User fills form** - - All fields except `id`, `is_verified`. - -2. **Server receives data** - - Validate required fields and unique constraints. - - Temporarily store as **unverified**. - -3. **PGP Verification** - - Server encrypts a message with user's PGP public key. - - Show **validation page** with encrypted message. - - User decrypts message and submits. - - Server validates ownership and sets `is_verified=True`. - -4. **Create Profile Page & Redirect** - - Generate user profile page with all info. - - Redirect user to main page or search page. - ---- - -## 3. Search Page - -- **Display**: Public user profiles as cards - - Show `profile_picture`, `firstname`, `lastname`, age, `country`, `city`. - -- **Filters**: - - All fields **except** `pgp`, `id`, `username`. - - Include boolean checks for presence of `email`, `phone`, `xmpp`. - -- **Profile Click** - - Open full profile page with all info, pictures, likes/dislikes. - ---- - -## 4. Login Flow - -1. User enters `username` + `PGP key`. -2. Server verifies PGP via challenge. -3. On success: - - User can edit **all fields** execpt `id` and `username`. - - User can change pgp key, new pgp key must be verified. - ---- - -## 5. Software Stack - -- **Software used**: - - Flask (backend framework) - - MySQL (Database) - - SQLAlchemy (ORM) - - python-gnupg (PGP validation) - - Flask-WTF (Forms validation) diff --git a/requirements.txt b/requirements.txt index 9524e22..b4fccf0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ Flask Flask-SQLAlchemy SQLAlchemy -PyMySQL python-gnupg diff --git a/src/__pycache__/main.cpython-314.pyc b/src/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000..a238518 Binary files /dev/null and b/src/__pycache__/main.cpython-314.pyc differ diff --git a/src/lovedb.db b/src/lovedb.db new file mode 100644 index 0000000..db65873 Binary files /dev/null and b/src/lovedb.db differ diff --git a/src/main.py b/src/main.py index 47f97ef..5e0753b 100644 --- a/src/main.py +++ b/src/main.py @@ -13,7 +13,7 @@ os.makedirs(UPLOAD_FOLDER, exist_ok=True) # creates the uploads directorie # configures the app app = Flask(__name__) # creates de app -app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://love:love@localhost:3309/lovedb' # database connection +app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.path.join(BASE_DIR, 'lovedb.db')}" # database connection app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # disable track modifications (for better performance) app.config['SECRET_KEY'] = 'random' # sets the secret key used to generate random numbers app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # sets the upload folder @@ -26,7 +26,7 @@ COUNTRIES = [ "Afghanistan","Albania","Algeria","Andorra","Angola","Antigua and "Barbados","Belarus","Belgium","Belize","Benin","Bhutan","Bolivia","Bosnia and Herzegovina", "Botswana","Brazil","Brunei","Bulgaria","Burkina Faso","Burundi","Cabo Verde","Cambodia", "Cameroon","Canada","Central African Republic","Chad","Chile","China","Colombia","Comoros", - "Congo (Congo-Brazzaville)","Costa Rica","Croatia","Cuba","Cyprus","Czechia (Czech Republic)", + "Congo (Congo-Brazzaville)","Costa Rica","Croatia","Cuba","Cyprus","Czechia (Czech Republic)", "Democratic Republic of the Congo","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador", "Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Eswatini (fmr. Swaziland)", "Ethiopia","Fiji","Finland","France","Gabon","Gambia","Georgia","Germany","Ghana","Greece", @@ -67,6 +67,7 @@ class User(db.Model): prefered_age_range = db.Column(db.String(20), nullable=True) likes = db.Column(db.JSON, nullable=True) dislikes = db.Column(db.JSON, nullable=True) + about = db.Column(db.String(4096), nullable=True) xmpp = db.Column(db.String(128), unique=True, nullable=False) email = db.Column(db.String(128), unique=True, nullable=True) phone = db.Column(db.String(20), unique=True, nullable=True) @@ -107,7 +108,7 @@ def pgp_encrypt_and_import(pgp_key: str, message: str): return None, None fingerprint = result.fingerprints[0] # encrypts message to the user's fingerprint - encrypted = gpg.encrypt(message, recipients=[fingerprint]) + encrypted = gpg.encrypt(message, recipients=[fingerprint], always_trust=True) if not encrypted.ok: return fingerprint, None return fingerprint, str(encrypted) @@ -154,10 +155,8 @@ def home(): if likes: likes_list = [x.strip().lower() for x in likes.split(",") if x.strip()] - for like in likes_list: - query = query.filter( - text(f"JSON_CONTAINS(likes, '\"{like}\"')") - ) + users = query.all() + users = [u for u in users if u.likes and all(l in u.likes for l in likes_list)] if dislikes: dislikes_list = [x.strip().lower() for x in dislikes.split(",") if x.strip()] diff --git a/src/static/drip.css b/src/static/drip.css index e48c8aa..5b595b4 100644 --- a/src/static/drip.css +++ b/src/static/drip.css @@ -5,6 +5,7 @@ font-style: normal; font-display: swap; } + @font-face { font-family: 'font'; src: url('/static/font/font-Bold.ttf') format('truetype'); @@ -17,12 +18,13 @@ body { background: #FFE0F4; color: #FF00AA; text-shadow: 0px 0px 5px rgba(255, 0, 170, 0.8); - padding: 5px; - max-width: 75%; - margin: auto; + padding: 10px; + max-width: 900px; + width: 100%; + margin: auto; font-family: font; font-weight: normal; - line-height: 1.2rem; + line-height: 1.4rem; word-wrap: break-word; font-size: 22px; } @@ -33,11 +35,9 @@ footer { margin-top: auto; } -main { -} - img { - max-width: 100%; + max-width: 100%; + height: auto; } strong, b { @@ -47,25 +47,18 @@ strong, b { section { margin-top: 32px; background: #fff; - padding: 5px; - border: medium; - border-color: #FF00AA; + padding: 10px; + border: medium dashed #FF00AA; border-radius: 5px; - border-style: dashed; } - h1 { color: #FF00AA; text-decoration: underline yellow; text-align: center; } -h2 { - color: #FF00AA; -} - -h3 { +h2, h3 { color: #FF00AA; } @@ -80,6 +73,8 @@ a:hover { table { width: 100%; border-collapse: collapse; + overflow-x: auto; + display: block; } th, td { @@ -94,5 +89,66 @@ th { } tr:nth-child(even) { - background-color: #FF00AA; + background-color: #FFB3DA; +} + +@media (max-width: 768px) { + + body { + font-size: 20px; + padding: 8px; + } + + section { + padding: 8px; + margin-top: 24px; + } + + h1 { + font-size: 1.8rem; + } + + h2 { + font-size: 1.4rem; + } + + h3 { + font-size: 1.2rem; + } + +} + +@media (max-width: 480px) { + + body { + font-size: 18px; + padding: 6px; + line-height: 1.5rem; + } + + section { + padding: 8px; + margin-top: 20px; + } + + h1 { + font-size: 1.5rem; + } + + h2 { + font-size: 1.2rem; + } + + h3 { + font-size: 1.1rem; + } + + table { + font-size: 14px; + } + + th, td { + padding: 6px; + } + } diff --git a/src/templates/page.html b/src/templates/page.html index 9cd5589..cde77d1 100644 --- a/src/templates/page.html +++ b/src/templates/page.html @@ -3,6 +3,7 @@ Dating Website +

Dating Website