This commit is contained in:
2025-07-10 21:04:55 +02:00
commit 6643edd61d
15 changed files with 18497 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+214
View File
@@ -0,0 +1,214 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
#poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
#pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
#pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
# Streamlit
.streamlit/secrets.toml
instance/
src/
instance/app_data.db
View File
+228
View File
@@ -0,0 +1,228 @@
import os
from flask import Flask, request, jsonify, session, send_from_directory
from flask_sqlalchemy import SQLAlchemy
import os
import requests
app = Flask(__name__, static_folder='../frontend/build', static_url_path='/')
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///manga.db'
db = SQLAlchemy(app)
# --- Database Models ---
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
class Manga(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), nullable=False)
api_id = db.Column(db.String(120), unique=True, nullable=False)
image_url = db.Column(db.String(255), nullable=True)
class UserManga(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
manga_id = db.Column(db.Integer, db.ForeignKey('manga.id'), nullable=False)
volume_number = db.Column(db.Integer, nullable=True) # New field for volume number
class Friend(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
friend_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
# --- API Routes ---
@app.route('/api/signup', methods=['POST'])
def signup():
data = request.get_json()
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'message': 'Username and password are required'}), 400
if User.query.filter_by(username=username).first():
return jsonify({'message': 'Username already exists'}), 409
new_user = User(username=username, password=password) # In a real app, hash passwords!
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'User created successfully'}), 201
@app.route('/api/login', methods=['POST'])
def login():
data = request.get_json()
username = data.get('username')
password = data.get('password')
user = User.query.filter_by(username=username, password=password).first() # In a real app, check hashed password
if user:
session['user_id'] = user.id
return jsonify({'message': 'Login successful', 'user_id': user.id}), 200
return jsonify({'message': 'Invalid credentials'}), 401
@app.route('/api/logout')
def logout():
session.pop('user_id', None)
return jsonify({'message': 'Logged out successfully'}), 200
@app.route('/api/dashboard')
def dashboard():
if 'user_id' not in session:
return jsonify({'message': 'Unauthorized'}), 401
user = User.query.get(session['user_id'])
if not user:
return jsonify({'message': 'User not found'}), 404
user_mangas = UserManga.query.filter_by(user_id=user.id).all()
grouped_user_mangas = {}
for um in user_mangas:
manga = Manga.query.get(um.manga_id)
if manga:
if manga.title not in grouped_user_mangas:
grouped_user_mangas[manga.title] = {
'manga_details': {
'id': manga.id,
'title': manga.title,
'api_id': manga.api_id,
'image_url': manga.image_url
},
'volumes': []
}
grouped_user_mangas[manga.title]['volumes'].append(um.volume_number)
grouped_user_mangas[manga.title]['volumes'].sort()
friends = Friend.query.filter_by(user_id=user.id).all()
friend_users = []
for f in friends:
friend_user = User.query.get(f.friend_id)
if friend_user:
friend_users.append({'id': friend_user.id, 'username': friend_user.username})
friend_mangas_data = {}
for friend_user_data in friend_users:
friend_id = friend_user_data['id']
friend_username = friend_user_data['username']
friend_user_mangas = UserManga.query.filter_by(user_id=friend_id).all()
grouped_friend_mangas = {}
for fum in friend_user_mangas:
fmanga = Manga.query.get(fum.manga_id)
if fmanga:
if fmanga.title not in grouped_friend_mangas:
grouped_friend_mangas[fmanga.title] = {
'manga_details': {
'id': fmanga.id,
'title': fmanga.title,
'api_id': fmanga.api_id,
'image_url': fmanga.image_url
},
'volumes': []
}
grouped_friend_mangas[fmanga.title]['volumes'].append(fum.volume_number)
grouped_friend_mangas[fmanga.title]['volumes'].sort()
friend_mangas_data[friend_username] = grouped_friend_mangas
return jsonify({
'user': {'id': user.id, 'username': user.username},
'mangas': grouped_user_mangas,
'friends': friend_users,
'friend_mangas': friend_mangas_data
}), 200
@app.route('/api/search', methods=['POST'])
def search():
if 'user_id' not in session:
return jsonify({'message': 'Unauthorized'}), 401
data = request.get_json()
manga_title = data.get('query')
if not manga_title:
return jsonify({'message': 'Query parameter is required'}), 400
response = requests.get(f'https://api.jikan.moe/v4/manga?q={manga_title}')
manga_data = response.json()
return jsonify(manga_data), 200
@app.route('/api/add_manga', methods=['POST'])
def add_manga():
if 'user_id' not in session:
return jsonify({'message': 'Unauthorized'}), 401
data = request.get_json()
manga_title = data.get('manga_title')
manga_api_id = data.get('manga_api_id')
image_url = data.get('image_url')
volume_number = data.get('volume_number')
if not manga_title or not manga_api_id:
return jsonify({'message': 'Manga title and API ID are required'}), 400
manga = Manga.query.filter_by(api_id=manga_api_id).first()
if not manga:
manga = Manga(title=manga_title, api_id=manga_api_id, image_url=image_url)
db.session.add(manga)
db.session.commit()
user_manga = UserManga.query.filter_by(
user_id=session['user_id'],
manga_id=manga.id,
volume_number=volume_number
).first()
if not user_manga:
user_manga = UserManga(user_id=session['user_id'], manga_id=manga.id, volume_number=volume_number)
db.session.add(user_manga)
db.session.commit()
return jsonify({'message': 'Manga added successfully'}), 201
@app.route('/api/search_user', methods=['POST'])
def search_user():
if 'user_id' not in session:
return jsonify({'message': 'Unauthorized'}), 401
data = request.get_json()
username = data.get('query')
if not username:
return jsonify({'message': 'Query parameter is required'}), 400
users = User.query.filter(User.username.like(f'%{username}%')).all()
users_data = [{'id': user.id, 'username': user.username} for user in users]
return jsonify({'users': users_data}), 200
@app.route('/api/add_friend', methods=['POST'])
def add_friend():
if 'user_id' not in session:
return jsonify({'message': 'Unauthorized'}), 401
data = request.get_json()
friend_id = data.get('friend_id')
if not friend_id:
return jsonify({'message': 'Friend ID is required'}), 400
friendship = Friend.query.filter_by(user_id=session['user_id'], friend_id=friend_id).first()
if not friendship:
new_friend = Friend(user_id=session['user_id'], friend_id=friend_id)
db.session.add(new_friend)
db.session.commit()
return jsonify({'message': 'Friend added successfully'}), 201
@app.route('/')
def serve_index():
return send_from_directory(app.static_folder, 'index.html')
@app.errorhandler(404)
def not_found(e):
return send_from_directory(app.static_folder, 'index.html')
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
+22
View File
@@ -0,0 +1,22 @@
body {
font-family: sans-serif;
margin: 20px;
}
.manga-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
}
.manga-card {
border: 1px solid #ccc;
padding: 10px;
text-align: center;
}
.manga-card img {
max-width: 100%;
height: auto;
}
+23
View File
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+17867
View File
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.3.7",
"react": "^19.1.0",
"react-bootstrap": "^2.10.10",
"react-dom": "^19.1.0",
"react-router-dom": "^7.6.3",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+43
View File
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+30
View File
@@ -0,0 +1,30 @@
altgraph @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/altgraph-0.17.2-py2.py3-none-any.whl
blinker==1.7.0
certifi==2025.7.9
charset-normalizer==3.4.2
click==8.1.7
Flask==3.0.2
Flask-SQLAlchemy==3.1.1
future @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/future-0.18.2-py3-none-any.whl
greenlet==3.0.3
gunicorn==21.2.0
idna==3.10
importlib_metadata==8.7.0
itsdangerous==2.1.2
Jinja2==3.1.3
macholib @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/macholib-1.15.2-py2.py3-none-any.whl
MarkupSafe==2.1.5
numpy==1.26.4
packaging==23.2
pandas==2.2.1
python-dateutil==2.9.0.post0
pytz==2024.1
requests==2.32.4
schedule==1.2.2
six==1.16.0
SQLAlchemy==2.0.28
typing_extensions==4.10.0
tzdata==2024.1
urllib3==2.5.0
Werkzeug==3.0.1
zipp==3.23.0