瀏覽代碼

first commit

zpenr 1 月之前
當前提交
8e36c45a0d
共有 100 個文件被更改,包括 1941 次插入0 次删除
  1. 3 0
      .dockerignore
  2. 7 0
      .env
  3. 5 0
      .flaskenv
  4. 7 0
      .vscode/launch.json
  5. 11 0
      Dockerfile
  6. 10 0
      app.ini
  7. 30 0
      app/__init__.py
  8. 二進制
      app/__pycache__/__init__.cpython-312.pyc
  9. 二進制
      app/__pycache__/config.cpython-312.pyc
  10. 二進制
      app/__pycache__/extensions.cpython-312.pyc
  11. 二進制
      app/__pycache__/extentions.cpython-312.pyc
  12. 二進制
      app/__pycache__/models.cpython-312.pyc
  13. 二進制
      app/__pycache__/roads.cpython-312.pyc
  14. 13 0
      app/config.py
  15. 5 0
      app/extensions.py
  16. 二進制
      app/models/__pycache__/__init__.cpython-312.pyc
  17. 二進制
      app/models/__pycache__/massage.cpython-312.pyc
  18. 二進制
      app/models/__pycache__/user.cpython-312.pyc
  19. 6 0
      app/models/massage.py
  20. 6 0
      app/models/user.py
  21. 二進制
      app/routes/__pycache__/chat_liza_me.cpython-312.pyc
  22. 二進制
      app/routes/__pycache__/chat_me_liza.cpython-312.pyc
  23. 二進制
      app/routes/__pycache__/chat_me_makar.cpython-312.pyc
  24. 二進制
      app/routes/__pycache__/main.cpython-312.pyc
  25. 二進制
      app/routes/__pycache__/massage.cpython-312.pyc
  26. 二進制
      app/routes/__pycache__/user.cpython-312.pyc
  27. 29 0
      app/routes/chat_liza_me.py
  28. 29 0
      app/routes/chat_me_liza.py
  29. 14 0
      app/routes/chat_me_makar.py
  30. 7 0
      app/routes/main.py
  31. 12 0
      app/routes/massage.py
  32. 11 0
      app/routes/user.py
  33. 6 0
      app/static/css/fonts.css
  34. 310 0
      app/static/css/main.css
  35. 二進制
      app/static/fonts/montseract/Montserrat-Black.ttf
  36. 二進制
      app/static/fonts/montseract/Montserrat-BlackItalic.ttf
  37. 二進制
      app/static/fonts/montseract/Montserrat-Bold.ttf
  38. 二進制
      app/static/fonts/montseract/Montserrat-BoldItalic.ttf
  39. 二進制
      app/static/fonts/montseract/Montserrat-ExtraBold.ttf
  40. 二進制
      app/static/fonts/montseract/Montserrat-ExtraBoldItalic.ttf
  41. 二進制
      app/static/fonts/montseract/Montserrat-ExtraLight.ttf
  42. 二進制
      app/static/fonts/montseract/Montserrat-ExtraLightItalic.ttf
  43. 二進制
      app/static/fonts/montseract/Montserrat-Italic.ttf
  44. 二進制
      app/static/fonts/montseract/Montserrat-Light.ttf
  45. 二進制
      app/static/fonts/montseract/Montserrat-LightItalic.ttf
  46. 二進制
      app/static/fonts/montseract/Montserrat-Medium.ttf
  47. 二進制
      app/static/fonts/montseract/Montserrat-MediumItalic.ttf
  48. 二進制
      app/static/fonts/montseract/Montserrat-Regular.ttf
  49. 二進制
      app/static/fonts/montseract/Montserrat-SemiBold.ttf
  50. 二進制
      app/static/fonts/montseract/Montserrat-SemiBoldItalic.ttf
  51. 二進制
      app/static/fonts/montseract/Montserrat-Thin.ttf
  52. 二進制
      app/static/fonts/montseract/Montserrat-ThinItalic.ttf
  53. 二進制
      app/static/images/635357373755808964.png
  54. 二進制
      app/static/images/avatars/Liza-avatar.jpg
  55. 二進制
      app/static/images/avatars/me_avatar.jpg
  56. 二進制
      app/static/images/avatars/мем макар.jpg
  57. 0 0
      app/static/scripts/main.js
  58. 76 0
      app/templates/chats/base.html
  59. 35 0
      app/templates/chats/lizame.html
  60. 35 0
      app/templates/chats/meliza.html
  61. 31 0
      app/templates/chats/memakar.html
  62. 62 0
      app/templates/main/base.html
  63. 5 0
      app/templates/main/index.html
  64. 42 0
      docker-cmpose.yml
  65. 0 0
      instance/project.db
  66. 1 0
      migrations/README
  67. 二進制
      migrations/__pycache__/env.cpython-312.pyc
  68. 50 0
      migrations/alembic.ini
  69. 113 0
      migrations/env.py
  70. 24 0
      migrations/script.py.mako
  71. 6 0
      ngnix/Dockerfile
  72. 10 0
      ngnix/ngnix.conf
  73. 18 0
      requirements.txt
  74. 6 0
      run.py
  75. 247 0
      venv/bin/Activate.ps1
  76. 70 0
      venv/bin/activate
  77. 27 0
      venv/bin/activate.csh
  78. 69 0
      venv/bin/activate.fish
  79. 8 0
      venv/bin/alembic
  80. 8 0
      venv/bin/dotenv
  81. 8 0
      venv/bin/flask
  82. 8 0
      venv/bin/mako-render
  83. 8 0
      venv/bin/pip
  84. 8 0
      venv/bin/pip3
  85. 8 0
      venv/bin/pip3.12
  86. 1 0
      venv/bin/python
  87. 1 0
      venv/bin/python3
  88. 1 0
      venv/bin/python3.12
  89. 二進制
      venv/bin/uwsgi
  90. 164 0
      venv/include/site/python3.12/greenlet/greenlet.h
  91. 1 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/INSTALLER
  92. 20 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/LICENSE
  93. 91 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/METADATA
  94. 31 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/RECORD
  95. 0 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/REQUESTED
  96. 5 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/WHEEL
  97. 1 0
      venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/top_level.txt
  98. 1 0
      venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER
  99. 28 0
      venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt
  100. 92 0
      venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/METADATA

+ 3 - 0
.dockerignore

@@ -0,0 +1,3 @@
+env/
+__pychache__/
+migrations/

+ 7 - 0
.env

@@ -0,0 +1,7 @@
+POSTGRES_USER = zpenr
+POSTGRES_PASSWORD = Qq12ww345
+POSTGRES_HOST = 127.0.0.1
+POSTGRES_PORT = 5532
+POSTGRES_DB = mydb
+
+

+ 5 - 0
.flaskenv

@@ -0,0 +1,5 @@
+FLASK_APP = app
+FLASK_ENV = development
+FLASK_DEBUG = True
+
+

+ 7 - 0
.vscode/launch.json

@@ -0,0 +1,7 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": []
+}

+ 11 - 0
Dockerfile

@@ -0,0 +1,11 @@
+FROM python:3.12.3
+
+WORKDIR /app
+
+ADD . /app
+
+RUN apt install gcc -y
+
+RUN pip install -r requirements.txt
+
+CMD ["uwsgi", "app.ini"]

+ 10 - 0
app.ini

@@ -0,0 +1,10 @@
+[uwsgi]
+wsgi-file = run.py
+callable = application
+socket = :8080
+processes = 4
+threads = 2
+master = true
+chmod-socket = 660
+vacuum = true
+die-on-term = true

+ 30 - 0
app/__init__.py

@@ -0,0 +1,30 @@
+from flask import Flask, render_template
+from .extensions import db, migrate
+from .config import Config
+
+from .routes.user import user
+from .routes.massage import massage
+from .routes.chat_me_liza import me_liza
+from .routes.chat_me_makar import me_makar
+from .routes.chat_liza_me import liza_me
+from .routes.main import main
+
+def create_app(config_class=Config):
+    app = Flask(__name__)    
+    app.config.from_object(config_class)
+
+    app.register_blueprint(user)
+    app.register_blueprint(massage)
+
+    app.register_blueprint(main)
+    app.register_blueprint(me_liza)
+    app.register_blueprint(liza_me)
+    app.register_blueprint(me_makar)
+
+    db.init_app(app)
+    migrate.init_app(app, db)
+
+    with app.app_context():
+        db.create_all()
+
+    return app

二進制
app/__pycache__/__init__.cpython-312.pyc


二進制
app/__pycache__/config.cpython-312.pyc


二進制
app/__pycache__/extensions.cpython-312.pyc


二進制
app/__pycache__/extentions.cpython-312.pyc


二進制
app/__pycache__/models.cpython-312.pyc


二進制
app/__pycache__/roads.cpython-312.pyc


+ 13 - 0
app/config.py

@@ -0,0 +1,13 @@
+import os
+
+class Config(object):
+    USER = os.environ.get('POSTGRES_USER', 'zpenr')
+    PASSWORD = os.environ.get('POSTGRES_PASSWORD', 'Qq12ww345')
+    HOST = os.environ.get('POSTGRES_HOST', '127.0.0.1')
+    PORT = os.environ.get('POSTGRES_PORT', 5532)
+    DB = os.environ.get('POSTGRES_DB', 'mydb')
+
+
+    SQLALCHEMY_DATABASE_URI = f'postgresql://{USER}:{PASSWORD}@{HOST}:{PORT}/{DB}'
+    SECRET_KEY = 'ewsdrftvgyhbujni12345uiasdjk1'
+    SQLALCHEMY_TRACK_MODIFICATIONS = True

+ 5 - 0
app/extensions.py

@@ -0,0 +1,5 @@
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+
+db = SQLAlchemy()
+migrate = Migrate()

二進制
app/models/__pycache__/__init__.cpython-312.pyc


二進制
app/models/__pycache__/massage.cpython-312.pyc


二進制
app/models/__pycache__/user.cpython-312.pyc


+ 6 - 0
app/models/massage.py

@@ -0,0 +1,6 @@
+from ..extensions import db
+
+class Massage(db.Model):
+    id = db.Column(db.Integer, primary_key = True)
+    author_of_massage = db.Column(db.String(100))
+    massage = db.Column(db.String(10000))

+ 6 - 0
app/models/user.py

@@ -0,0 +1,6 @@
+from ..extensions import db
+
+
+class User(db.Model):
+    id = db.Column(db.Integer, primary_key = True)
+    name = db.Column(db.String(50))

二進制
app/routes/__pycache__/chat_liza_me.cpython-312.pyc


二進制
app/routes/__pycache__/chat_me_liza.cpython-312.pyc


二進制
app/routes/__pycache__/chat_me_makar.cpython-312.pyc


二進制
app/routes/__pycache__/main.cpython-312.pyc


二進制
app/routes/__pycache__/massage.cpython-312.pyc


二進制
app/routes/__pycache__/user.cpython-312.pyc


+ 29 - 0
app/routes/chat_liza_me.py

@@ -0,0 +1,29 @@
+from flask import Blueprint, render_template, request, redirect
+from ..models.massage import Massage
+from ..extensions import db
+
+liza_me = Blueprint('liza_me', __name__)
+
+@liza_me.route('/chat_liza_me', methods=['POST', 'GET'])
+def chat_liza_me():
+    if request.method == "POST":
+        mess = request.form['mess']
+        if len(mess)>0:
+            message = Massage( massage = mess, author_of_massage = 'liza')
+
+            try:
+                db.session.add(message)
+                db.session.commit()
+                
+                return redirect('/chat_liza_me')
+            except Exception as e:
+                print(str(e))
+        else: 
+            messages = Massage.query.all()
+            last_message = messages[-1].massage
+        return render_template('chats/lizame.html', messages = messages, last_message=last_message)
+        
+    else:
+        messages = Massage.query.all()
+        last_message = messages[-1].massage
+        return render_template('chats/lizame.html', messages = messages, last_message=last_message)

+ 29 - 0
app/routes/chat_me_liza.py

@@ -0,0 +1,29 @@
+from flask import Blueprint, render_template, request, redirect
+from ..models.massage import Massage
+from ..extensions import db
+
+me_liza = Blueprint('me_liza', __name__)
+
+@me_liza.route('/chat_me_liza', methods=['POST', 'GET'])
+def chat_me_liza():
+    if request.method == "POST":
+        mess = request.form['mess']
+        if len(mess)>0:
+            message = Massage( massage = mess, author_of_massage = 'me')
+
+            try:
+                db.session.add(message)
+                db.session.commit()
+                
+                return redirect('/chat_me_liza')
+            except Exception as e:
+                print(str(e))
+        else: 
+            messages = Massage.query.all()
+            last_message = messages[-1].massage
+        return render_template('chats/meliza.html', messages = messages, last_message=last_message)
+        
+    else:
+        messages = Massage.query.all()
+        last_message = messages[-1].massage
+        return render_template('chats/meliza.html', messages = messages, last_message=last_message)

+ 14 - 0
app/routes/chat_me_makar.py

@@ -0,0 +1,14 @@
+from flask import Blueprint, render_template, request, redirect
+from ..models.massage import Massage
+from ..extensions import db
+
+me_makar = Blueprint('me_makar', __name__)
+
+@me_makar.route('/chat_me_makar', methods=['POST', 'GET'])
+def chat_me_makar():
+    if request.method == "POST":
+        mess = request.form['mess']
+        print(f"Сообщение от пользователя (Макар): {mess}")  # Вывод в консоль Python
+        return redirect('/chat_me_makar')
+    else:
+        return render_template('chats/memakar.html')

+ 7 - 0
app/routes/main.py

@@ -0,0 +1,7 @@
+from flask import Blueprint, render_template
+
+main = Blueprint('main', __name__)
+
+@main.route('/')
+def index():
+    return render_template('main/index.html')

+ 12 - 0
app/routes/massage.py

@@ -0,0 +1,12 @@
+from flask import Blueprint, render_template, request, redirect
+from ..models.massage import Massage
+from ..extensions import db
+massage = Blueprint('massage', __name__)
+
+# @massage.route('/chat_me_liza', methods = ['POST','GET'])
+# def create():
+#     if request.method == "POST":
+#         mess = request.form['mess']
+#         print(mess)
+#         return redirect('/chat_me_liza')
+#     else: return render_template('chats/meliza.html')

+ 11 - 0
app/routes/user.py

@@ -0,0 +1,11 @@
+from flask import Blueprint
+from ..models.user import User
+from ..extensions import db
+user = Blueprint('user', __name__)
+
+@user.route('/user/<name>')
+def create_user(name):
+    user = User(name=name)
+    db.session.add(user)
+    db.session.commit()
+    return 'User created!!!'

+ 6 - 0
app/static/css/fonts.css

@@ -0,0 +1,6 @@
+@font-face {
+    font-family: 'Montserrat';
+    font-style: normal;
+    font-weight: 400;
+    src: url('../fonts/montseract/Montserrat-Regular.ttf');
+}

+ 310 - 0
app/static/css/main.css

@@ -0,0 +1,310 @@
+@import url('fonts.css');
+
+/* ===== BASE STYLES ===== */
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    overflow: hidden;
+    font-family: 'Montserrat', 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
+    font-weight: 400;
+    background-color: rgb(31, 25, 60);
+    height: 100vh;
+}
+
+a {
+    text-decoration: none;
+    color: inherit;
+}
+
+/* ===== LAYOUT ===== */
+.app-container {
+    display: flex;
+    height: 100vh;
+    background-color: rgb(31, 25, 60);
+}
+
+/* ===== SIDEBAR ===== */
+.sidebar {
+    width: 350px;
+    min-width: 300px;
+    background-color: rgb(26, 21, 50);
+    border-right: 2px solid rgb(45, 55, 89);
+    display: flex;
+    flex-direction: column;
+    height: 100vh;
+}
+
+.settings-header {
+    padding: 15px;
+    border-bottom: 1px solid rgb(45, 55, 89);
+}
+
+.menu {
+    background: transparent;
+    border: none;
+    cursor: pointer;
+    padding: 8px;
+    border-radius: 5px;
+    transition: background-color 0.3s ease;
+}
+
+.menu:hover {
+    background-color: rgb(43, 35, 82);
+}
+
+.settings-icon {
+    width: 30px;
+    height: 30px;
+    object-fit: cover;
+}
+
+.chats-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 10px 0;
+}
+
+/* ===== CHAT ITEM ===== */
+.chat-elem {
+    display: flex;
+    align-items: center;
+    padding: 15px;
+    transition: background-color 0.3s ease;
+    border-bottom: 1px solid rgba(45, 55, 89, 0.5);
+}
+
+.chat-elem:hover {
+    background-color: rgb(43, 35, 82);
+}
+
+.avatar-image {
+    width: 50px;
+    height: 50px;
+    border-radius: 50%;
+    object-fit: cover;
+    margin-right: 15px;
+    flex-shrink: 0;
+}
+
+.chat-info {
+    flex: 1;
+    min-width: 0;
+}
+
+.chat-name {
+    font-size: 16px;
+    color: rgb(229, 228, 238);
+    margin-bottom: 5px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.last-massege {
+    font-size: 13px;
+    color: rgb(204, 204, 204);
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+/* ===== MAIN CHAT AREA ===== */
+.chat-area {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    height: 100vh;
+    background-color: rgb(35, 29, 70);
+}
+
+.chat-header {
+    padding: 15px 20px;
+    border-bottom: 2px solid rgb(57, 70, 112);
+    background-color: rgb(26, 21, 50);
+}
+
+.chat-title {
+    font-size: 18px;
+    color: rgb(229, 228, 238);
+}
+
+/* ===== MESSAGES CONTAINER ===== */
+.massages {
+    flex: 1;
+    overflow-y: auto;
+    padding: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+    max-height: calc(100vh - 140px);
+}
+
+/* ===== MESSAGE STYLES ===== */
+.massage {
+    display: flex;
+    align-items: flex-start;
+    gap: 10px;
+    max-width: 70%;
+}
+
+.massage-from-me {
+    align-self: flex-end;
+    flex-direction: row-reverse;
+}
+
+.avatar-massage {
+    width: 35px;
+    height: 35px;
+    border-radius: 50%;
+    object-fit: cover;
+    flex-shrink: 0;
+    margin-top: 5px;
+}
+
+.message-content {
+    background-color: rgb(53, 20, 161);
+    padding: 12px 15px;
+    border-radius: 18px;
+    max-width: 100%;
+    word-wrap: break-word;
+}
+
+.massage-from-me .message-content {
+    background-color: rgb(82, 55, 240);
+}
+
+.massage-text {
+    color: rgb(229, 228, 238);
+    margin: 0;
+    line-height: 1.4;
+    font-size: 14px;
+}
+
+/* ===== MESSAGE INPUT ===== */
+.message-input-container {
+    padding: 20px;
+    background-color: rgb(26, 21, 50);
+    border-top: 2px solid rgb(57, 70, 112);
+}
+
+.type-massage {
+    display: flex;
+    gap: 10px;
+    align-items: center;
+}
+
+.type-massage-input {
+    flex: 1;
+    height: 45px;
+    padding: 0 15px;
+    border: 2px solid rgb(45, 55, 89);
+    border-radius: 25px;
+    background-color: rgb(68, 83, 130);
+    color: rgb(229, 228, 238);
+    font-size: 14px;
+    outline: none;
+    transition: border-color 0.3s ease;
+}
+
+.type-massage-input:focus {
+    border-color: rgb(82, 55, 240);
+}
+
+.type-massage-input::placeholder {
+    color: rgb(204, 204, 204);
+}
+
+.send-massage {
+    height: 45px;
+    padding: 0 20px;
+    background-color: rgb(82, 55, 240);
+    color: rgb(229, 228, 238);
+    border: none;
+    border-radius: 25px;
+    cursor: pointer;
+    font-size: 14px;
+    transition: background-color 0.3s ease;
+}
+
+.send-massage:hover {
+    background-color: rgb(102, 75, 255);
+}
+
+/* ===== SCROLLBAR ===== */
+.massages::-webkit-scrollbar {
+    width: 8px;
+}
+
+.massages::-webkit-scrollbar-track {
+    background: rgba(45, 55, 89, 0.3);
+    border-radius: 4px;
+    margin: 5px 0;
+}
+
+.massages::-webkit-scrollbar-thumb {
+    background: linear-gradient(180deg, 
+        rgba(82, 55, 240, 0.8) 0%, 
+        rgba(53, 20, 161, 0.8) 100%);
+    border-radius: 4px;
+    border: 2px solid transparent;
+    background-clip: content-box;
+}
+
+.massages::-webkit-scrollbar-thumb:hover {
+    background: linear-gradient(180deg, 
+        rgba(102, 75, 255, 0.9) 0%, 
+        rgba(73, 40, 181, 0.9) 100%);
+}
+
+.chats-list::-webkit-scrollbar {
+    width: 6px;
+}
+
+.chats-list::-webkit-scrollbar-thumb {
+    background: rgba(255, 255, 255, 0.2);
+    border-radius: 3px;
+}
+
+/* ===== RESPONSIVE DESIGN ===== */
+@media (max-width: 768px) {
+    .app-container {
+        flex-direction: column;
+    }
+    
+    .sidebar {
+        width: 100%;
+        height: auto;
+        max-height: 40vh;
+    }
+    
+    .chat-area {
+        height: 60vh;
+    }
+    
+    .massage {
+        max-width: 85%;
+    }
+    
+    .type-massage-input {
+        font-size: 16px; /* Prevent zoom on iOS */
+    }
+}
+
+/* ===== UTILITY CLASSES ===== */
+.no-messages {
+    text-align: center;
+    color: rgb(204, 204, 204);
+    padding: 40px 20px;
+    font-style: italic;
+}
+
+.loading {
+    display: flex;
+    justify-content: center;
+    padding: 20px;
+    color: rgb(204, 204, 204);
+}

二進制
app/static/fonts/montseract/Montserrat-Black.ttf


二進制
app/static/fonts/montseract/Montserrat-BlackItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-Bold.ttf


二進制
app/static/fonts/montseract/Montserrat-BoldItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-ExtraBold.ttf


二進制
app/static/fonts/montseract/Montserrat-ExtraBoldItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-ExtraLight.ttf


二進制
app/static/fonts/montseract/Montserrat-ExtraLightItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-Italic.ttf


二進制
app/static/fonts/montseract/Montserrat-Light.ttf


二進制
app/static/fonts/montseract/Montserrat-LightItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-Medium.ttf


二進制
app/static/fonts/montseract/Montserrat-MediumItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-Regular.ttf


二進制
app/static/fonts/montseract/Montserrat-SemiBold.ttf


二進制
app/static/fonts/montseract/Montserrat-SemiBoldItalic.ttf


二進制
app/static/fonts/montseract/Montserrat-Thin.ttf


二進制
app/static/fonts/montseract/Montserrat-ThinItalic.ttf


二進制
app/static/images/635357373755808964.png


二進制
app/static/images/avatars/Liza-avatar.jpg


二進制
app/static/images/avatars/me_avatar.jpg


二進制
app/static/images/avatars/мем макар.jpg


+ 0 - 0
app/static/scripts/main.js


+ 76 - 0
app/templates/chats/base.html

@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="ru">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link href="{{ url_for('static', filename='css/main.css') }}" type="text/css" rel="stylesheet">
+    <title>Zpenr messenger - Чат</title>
+</head>
+<body>
+    <div class="app-container">
+        <!-- Левая панель с чатами -->
+        <div class="sidebar">
+            <div class="settings-header">
+                <button class="menu">
+                    <img src="{{ url_for('static', filename='images/635357373755808964.png') }}" class="settings-icon" alt="Меню">
+                </button>
+            </div>
+            
+            <div class="chats-list">
+                <a href="{{ url_for('me_liza.chat_me_liza') }}" class="chat-elem">
+                    <img src="{{ url_for('static', filename='images/avatars/Liza-avatar.jpg') }}" class="avatar-image" alt="Лиза">
+                    <div class="chat-info">
+                        <div class="chat-name">
+                            <p class="name-of-chat">{% block nameofchat %}{% endblock %}</p>
+                        </div>
+                        <div class="last-massege">{% block lastmassege %}{% endblock %}</div>
+                    </div>
+                </a>
+                
+                <a href="{{ url_for('me_makar.chat_me_makar') }}" class="chat-elem">
+                    <img src="{{ url_for('static', filename='images/avatars/мем макар.jpg') }}" class="avatar-image" alt="BardMakar">
+                    <div class="chat-info">
+                        <div class="chat-name">
+                            <p class="name-of-chat">BardMakar</p>
+                        </div>
+                        <div class="last-massege">Hello, maaaan!!!</div>
+                    </div>
+                </a>
+            </div>
+        </div>
+
+        <!-- Область чата -->
+        <div class="chat-area">
+            <div class="chat-header">
+                <h1 class="chat-title"></h1>
+            </div>
+            
+            <div class="massages" id="messagesContainer">
+                {% block messages %}
+                <!-- Сообщения будут здесь -->
+                {% endblock %}
+            </div>
+
+            <!-- Форма ввода сообщений -->
+            <div class="message-input-container">
+                {% block form %}
+                <form method="post" class="type-massage" id="massageForm">
+                    <input name="mess" type="text" class="type-massage-input" placeholder="Введите сообщение...">
+                    <button type="submit" class="send-massage">Отправить</button>
+                </form>
+                {% endblock %}
+            </div>
+        </div>
+    </div>
+
+    <script>
+        // Автопрокрутка вниз при загрузке
+        document.addEventListener('DOMContentLoaded', function() {
+            const messagesContainer = document.getElementById('messagesContainer');
+            if (messagesContainer) {
+                messagesContainer.scrollTop = messagesContainer.scrollHeight;
+            }
+        });
+    </script>
+</body>
+</html>

+ 35 - 0
app/templates/chats/lizame.html

@@ -0,0 +1,35 @@
+{% extends 'chats/base.html' %}
+
+{% block nameofchat %}Лиза{% endblock %}
+
+{% block lastmassege %}
+    {{ last_message if last_message else "Нет сообщений" }}
+{% endblock %}
+
+{% block messages %}
+    {% if messages %}
+        {% for message in messages %}
+            {% if message.author_of_massage == 'liza' %}
+            <div class="massage">
+                <img src="{{ url_for('static', filename='images/avatars/Liza-avatar.jpg') }}" class="avatar-massage" alt="Лиза">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+            </div>
+            {% endif %}
+
+            {% if message.author_of_massage == 'me' %}
+            <div class="massage massage-from-me">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+                <img src="{{ url_for('static', filename='images/avatars/me_avatar.jpg') }}" class="avatar-massage" alt="Я">
+            </div>
+            {% endif %}
+        {% endfor %}
+    {% else %}
+        <div class="no-messages">
+            <p>Пока нет сообщений с Лизой</p>
+        </div>
+    {% endif %}
+{% endblock %}

+ 35 - 0
app/templates/chats/meliza.html

@@ -0,0 +1,35 @@
+{% extends 'chats/base.html' %}
+
+{% block nameofchat %}Лиза{% endblock %}
+
+{% block lastmassege %}
+    {{ last_message if last_message else "Нет сообщений" }}
+{% endblock %}
+
+{% block messages %}
+    {% if messages %}
+        {% for message in messages %}
+            {% if message.author_of_massage == 'me' %}
+            <div class="massage massage-from-me">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+                <img src="{{ url_for('static', filename='images/avatars/me_avatar.jpg') }}" class="avatar-massage" alt="Я">
+            </div>
+            {% endif %}
+
+            {% if message.author_of_massage == 'liza' %}
+            <div class="massage">
+                <img src="{{ url_for('static', filename='images/avatars/Liza-avatar.jpg') }}" class="avatar-massage" alt="Лиза">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+            </div>
+            {% endif %}
+        {% endfor %}
+    {% else %}
+        <div class="no-messages">
+            <p>Пока нет сообщений с Лизой</p>
+        </div>
+    {% endif %}
+{% endblock %}

+ 31 - 0
app/templates/chats/memakar.html

@@ -0,0 +1,31 @@
+{% extends 'chats/base.html' %}
+
+{% block nameofchat %}BardMakar{% endblock %}
+
+{% block lastmassege %}Hello, maaaan!!!{% endblock %}
+
+{% block messages %}
+    {% if messages %}
+        {% for message in messages %}
+            {% if message.author_of_massage == 'me' %}
+            <div class="massage massage-from-me">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+                <img src="{{ url_for('static', filename='images/avatars/me_avatar.jpg') }}" class="avatar-massage" alt="Я">
+            </div>
+            {% else %}
+            <div class="massage">
+                <img src="{{ url_for('static', filename='images/avatars/мем макар.jpg') }}" class="avatar-massage" alt="BardMakar">
+                <div class="message-content">
+                    <p class="massage-text">{{ message.massage }}</p>
+                </div>
+            </div>
+            {% endif %}
+        {% endfor %}
+    {% else %}
+        <div class="no-messages">
+            <p>Пока нет сообщений с BardMakar</p>
+        </div>
+    {% endif %}
+{% endblock %}

+ 62 - 0
app/templates/main/base.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="ru">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link href="{{ url_for('static', filename='css/main.css') }}" type="text/css" rel="stylesheet">
+    <title>Zpenr messenger</title>
+</head>
+<body>
+    <div class="app-container">
+        <!-- Левая панель с чатами -->
+        <div class="sidebar">
+            <div class="settings-header">
+                <button class="menu">
+                    <img src="{{ url_for('static', filename='images/635357373755808964.png') }}" class="settings-icon" alt="Меню">
+                </button>
+            </div>
+            
+            <div class="chats-list">
+                <a href="{{ url_for('me_liza.chat_me_liza') }}" class="chat-elem">
+                    <img src="{{ url_for('static', filename='images/avatars/Liza-avatar.jpg') }}" class="avatar-image" alt="Лиза">
+                    <div class="chat-info">
+                        <div class="chat-name">
+                            <p class="name-of-chat">{% block nameofchat %}{% endblock %}</p>
+                        </div>
+                        <div class="last-massege">{% block lastmassege %}{% endblock %}</div>
+                    </div>
+                </a>
+                
+                <a href="{{ url_for('me_makar.chat_me_makar') }}" class="chat-elem">
+                    <img src="{{ url_for('static', filename='images/avatars/мем макар.jpg') }}" class="avatar-image" alt="BardMakar">
+                    <div class="chat-info">
+                        <div class="chat-name">
+                            <p class="name-of-chat">BardMakar</p>
+                        </div>
+                        <div class="last-massege">Hello, maaaan!!!</div>
+                    </div>
+                </a>
+            </div>
+        </div>
+
+        <!-- Область чата -->
+        <div class="chat-area">
+            <div class="chat-header">
+                <h1 class="chat-title"></h1>
+            </div>
+            
+            <div class="massages" id="messagesContainer">
+                {% block messages %}
+                <!-- Сообщения будут здесь -->
+                {% endblock %}
+            </div>
+
+            {% block form %}
+            <!-- Форма ввода будет здесь -->
+            {% endblock %}
+        </div>
+    </div>
+
+    <script src="{{ url_for('static', filename='scripts/main.js') }}"></script>
+</body>
+</html>

+ 5 - 0
app/templates/main/index.html

@@ -0,0 +1,5 @@
+{% extends 'main/base.html' %}
+
+{% block nameofchat%}Лиза{% endblock %}
+
+{% block lastmassege%}Я тебя люблю{% endblock %}

+ 42 - 0
docker-cmpose.yml

@@ -0,0 +1,42 @@
+version: "3.9"
+
+services:
+  postgres:
+    image: postgres
+    container_name: postgres
+    volums: 
+      - ~/.pg/pg_data/flask-yt:/var/lib/postgresql/data
+    env_file:
+      - .env
+    ports:
+      - "54321:5432"
+  flask: 
+    build:
+      dockerfile: Dockerfile
+      context: .
+    container_name: flask
+    volums:
+      - ~/.uploads/images/flask-yt:/app/app/static/upload
+    env_file:
+      - .env
+    environment:
+      - POSTGRES_HOST=${POSTGRES_HOST}
+      - POSTGRES_PORT=${POSTGRES_PORT}
+      - POSTGRES_USER=${POSTGRES_USER}
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+      - POSTGRES_DB=${POSTGRES_DB}
+    depends_on:
+      - postgres
+    expose:
+      - 8080
+  ngnix:
+    build:
+      dockerfile: ./Dockerfile
+      context: ./ngnix/
+    container_name: ngnix
+    env_file:
+      - .env
+    depends_on:
+      - flask
+    ports:
+      - "8080:80"

+ 0 - 0
instance/project.db


+ 1 - 0
migrations/README

@@ -0,0 +1 @@
+Single-database configuration for Flask.

二進制
migrations/__pycache__/env.cpython-312.pyc


+ 50 - 0
migrations/alembic.ini

@@ -0,0 +1,50 @@
+# A generic, single database configuration.
+
+[alembic]
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic,flask_migrate
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[logger_flask_migrate]
+level = INFO
+handlers =
+qualname = flask_migrate
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S

+ 113 - 0
migrations/env.py

@@ -0,0 +1,113 @@
+import logging
+from logging.config import fileConfig
+
+from flask import current_app
+
+from alembic import context
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+logger = logging.getLogger('alembic.env')
+
+
+def get_engine():
+    try:
+        # this works with Flask-SQLAlchemy<3 and Alchemical
+        return current_app.extensions['migrate'].db.get_engine()
+    except (TypeError, AttributeError):
+        # this works with Flask-SQLAlchemy>=3
+        return current_app.extensions['migrate'].db.engine
+
+
+def get_engine_url():
+    try:
+        return get_engine().url.render_as_string(hide_password=False).replace(
+            '%', '%%')
+    except AttributeError:
+        return str(get_engine().url).replace('%', '%%')
+
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+config.set_main_option('sqlalchemy.url', get_engine_url())
+target_db = current_app.extensions['migrate'].db
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def get_metadata():
+    if hasattr(target_db, 'metadatas'):
+        return target_db.metadatas[None]
+    return target_db.metadata
+
+
+def run_migrations_offline():
+    """Run migrations in 'offline' mode.
+
+    This configures the context with just a URL
+    and not an Engine, though an Engine is acceptable
+    here as well.  By skipping the Engine creation
+    we don't even need a DBAPI to be available.
+
+    Calls to context.execute() here emit the given string to the
+    script output.
+
+    """
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(
+        url=url, target_metadata=get_metadata(), literal_binds=True
+    )
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+def run_migrations_online():
+    """Run migrations in 'online' mode.
+
+    In this scenario we need to create an Engine
+    and associate a connection with the context.
+
+    """
+
+    # this callback is used to prevent an auto-migration from being generated
+    # when there are no changes to the schema
+    # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
+    def process_revision_directives(context, revision, directives):
+        if getattr(config.cmd_opts, 'autogenerate', False):
+            script = directives[0]
+            if script.upgrade_ops.is_empty():
+                directives[:] = []
+                logger.info('No changes in schema detected.')
+
+    conf_args = current_app.extensions['migrate'].configure_args
+    if conf_args.get("process_revision_directives") is None:
+        conf_args["process_revision_directives"] = process_revision_directives
+
+    connectable = get_engine()
+
+    with connectable.connect() as connection:
+        context.configure(
+            connection=connection,
+            target_metadata=get_metadata(),
+            **conf_args
+        )
+
+        with context.begin_transaction():
+            context.run_migrations()
+
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()

+ 24 - 0
migrations/script.py.mako

@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade():
+    ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+    ${downgrades if downgrades else "pass"}

+ 6 - 0
ngnix/Dockerfile

@@ -0,0 +1,6 @@
+FROM ngnix
+
+RUN rm /etc/ngnix/conf.d/default.conf
+
+COPY ngnix.conf /etc/ngnix/conf.d/
+

+ 10 - 0
ngnix/ngnix.conf

@@ -0,0 +1,10 @@
+server{
+    listen 80;
+
+    client_max_body_size 1G;
+
+    location / {
+        include uwsgi_params;
+        uwsgi_pass flask:8080;
+    }
+}

+ 18 - 0
requirements.txt

@@ -0,0 +1,18 @@
+alembic==1.16.5
+blinker==1.9.0
+click==8.3.0
+Flask==3.1.2
+Flask-Migrate==4.1.0
+Flask-SQLAlchemy==3.1.1
+greenlet==3.2.4
+itsdangerous==2.2.0
+Jinja2==3.1.6
+Mako==1.3.10
+MarkupSafe==3.0.2
+psycopg2==2.9.10
+psycopg2-binary==2.9.10
+python-dotenv==1.1.1
+SQLAlchemy==2.0.43
+typing_extensions==4.15.0
+uWSGI==2.0.30
+Werkzeug==3.1.3

+ 6 - 0
run.py

@@ -0,0 +1,6 @@
+import os
+from app import create_app
+from dotenv import load_dotenv
+load_dotenv('.env')
+
+application = create_app()

+ 247 - 0
venv/bin/Activate.ps1

@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies: 
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+    [Parameter(Mandatory = $false)]
+    [String]
+    $VenvDir,
+    [Parameter(Mandatory = $false)]
+    [String]
+    $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+    # Revert to original values
+
+    # The prior prompt:
+    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+    }
+
+    # The prior PYTHONHOME:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+    }
+
+    # The prior PATH:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+    }
+
+    # Just remove the VIRTUAL_ENV altogether:
+    if (Test-Path -Path Env:VIRTUAL_ENV) {
+        Remove-Item -Path env:VIRTUAL_ENV
+    }
+
+    # Just remove VIRTUAL_ENV_PROMPT altogether.
+    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+    }
+
+    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+    }
+
+    # Leave deactivate function in the global namespace if requested:
+    if (-not $NonDestructive) {
+        Remove-Item -Path function:deactivate
+    }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+    [String]
+    $ConfigDir
+) {
+    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+    # An empty map will be returned if no config file is found.
+    $pyvenvConfig = @{ }
+
+    if ($pyvenvConfigPath) {
+
+        Write-Verbose "File exists, parse `key = value` lines"
+        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+        $pyvenvConfigContent | ForEach-Object {
+            $keyval = $PSItem -split "\s*=\s*", 2
+            if ($keyval[0] -and $keyval[1]) {
+                $val = $keyval[1]
+
+                # Remove extraneous quotations around a string value.
+                if ("'""".Contains($val.Substring(0, 1))) {
+                    $val = $val.Substring(1, $val.Length - 2)
+                }
+
+                $pyvenvConfig[$keyval[0]] = $val
+                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+            }
+        }
+    }
+    return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+    Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+    Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+        $Prompt = $pyvenvCfg['prompt'];
+    }
+    else {
+        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+        $Prompt = Split-Path -Path $venvDir -Leaf
+    }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+    Write-Verbose "Setting prompt to '$Prompt'"
+
+    # Set the prompt to include the env name
+    # Make sure _OLD_VIRTUAL_PROMPT is global
+    function global:_OLD_VIRTUAL_PROMPT { "" }
+    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+    function global:prompt {
+        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+        _OLD_VIRTUAL_PROMPT
+    }
+    $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+    Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"

+ 70 - 0
venv/bin/activate

@@ -0,0 +1,70 @@
+# This file must be used with "source bin/activate" *from bash*
+# You cannot run it directly
+
+deactivate () {
+    # reset old environment variables
+    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+        PATH="${_OLD_VIRTUAL_PATH:-}"
+        export PATH
+        unset _OLD_VIRTUAL_PATH
+    fi
+    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+        export PYTHONHOME
+        unset _OLD_VIRTUAL_PYTHONHOME
+    fi
+
+    # Call hash to forget past commands. Without forgetting
+    # past commands the $PATH changes we made may not be respected
+    hash -r 2> /dev/null
+
+    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+        PS1="${_OLD_VIRTUAL_PS1:-}"
+        export PS1
+        unset _OLD_VIRTUAL_PS1
+    fi
+
+    unset VIRTUAL_ENV
+    unset VIRTUAL_ENV_PROMPT
+    if [ ! "${1:-}" = "nondestructive" ] ; then
+    # Self destruct!
+        unset -f deactivate
+    fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+# on Windows, a path can contain colons and backslashes and has to be converted:
+if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
+    # transform D:\path\to\venv to /d/path/to/venv on MSYS
+    # and to /cygdrive/d/path/to/venv on Cygwin
+    export VIRTUAL_ENV=$(cygpath /home/zpenr/project/venv)
+else
+    # use the path as-is
+    export VIRTUAL_ENV=/home/zpenr/project/venv
+fi
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/"bin":$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+    unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+    _OLD_VIRTUAL_PS1="${PS1:-}"
+    PS1='(venv) '"${PS1:-}"
+    export PS1
+    VIRTUAL_ENV_PROMPT='(venv) '
+    export VIRTUAL_ENV_PROMPT
+fi
+
+# Call hash to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+hash -r 2> /dev/null

+ 27 - 0
venv/bin/activate.csh

@@ -0,0 +1,27 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+
+# Created by Davide Di Blasi <davidedb@gmail.com>.
+# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV /home/zpenr/project/venv
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+    set prompt = '(venv) '"$prompt"
+    setenv VIRTUAL_ENV_PROMPT '(venv) '
+endif
+
+alias pydoc python -m pydoc
+
+rehash

+ 69 - 0
venv/bin/activate.fish

@@ -0,0 +1,69 @@
+# This file must be used with "source <venv>/bin/activate.fish" *from fish*
+# (https://fishshell.com/). You cannot run it directly.
+
+function deactivate  -d "Exit virtual environment and return to normal shell environment"
+    # reset old environment variables
+    if test -n "$_OLD_VIRTUAL_PATH"
+        set -gx PATH $_OLD_VIRTUAL_PATH
+        set -e _OLD_VIRTUAL_PATH
+    end
+    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+        set -e _OLD_VIRTUAL_PYTHONHOME
+    end
+
+    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+        set -e _OLD_FISH_PROMPT_OVERRIDE
+        # prevents error when using nested fish instances (Issue #93858)
+        if functions -q _old_fish_prompt
+            functions -e fish_prompt
+            functions -c _old_fish_prompt fish_prompt
+            functions -e _old_fish_prompt
+        end
+    end
+
+    set -e VIRTUAL_ENV
+    set -e VIRTUAL_ENV_PROMPT
+    if test "$argv[1]" != "nondestructive"
+        # Self-destruct!
+        functions -e deactivate
+    end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV /home/zpenr/project/venv
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/"bin $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+    set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+    # fish uses a function instead of an env var to generate the prompt.
+
+    # Save the current fish_prompt function as the function _old_fish_prompt.
+    functions -c fish_prompt _old_fish_prompt
+
+    # With the original prompt function renamed, we can override with our own.
+    function fish_prompt
+        # Save the return status of the last command.
+        set -l old_status $status
+
+        # Output the venv prompt; color taken from the blue of the Python logo.
+        printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
+
+        # Restore the return status of the previous command.
+        echo "exit $old_status" | .
+        # Output the original/"old" prompt.
+        _old_fish_prompt
+    end
+
+    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+    set -gx VIRTUAL_ENV_PROMPT '(venv) '
+end

+ 8 - 0
venv/bin/alembic

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from alembic.config import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
venv/bin/dotenv

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from dotenv.__main__ import cli
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli())

+ 8 - 0
venv/bin/flask

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from flask.cli import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
venv/bin/mako-render

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from mako.cmd import cmdline
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cmdline())

+ 8 - 0
venv/bin/pip

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
venv/bin/pip3

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
venv/bin/pip3.12

@@ -0,0 +1,8 @@
+#!/home/zpenr/project/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 1 - 0
venv/bin/python

@@ -0,0 +1 @@
+python3

+ 1 - 0
venv/bin/python3

@@ -0,0 +1 @@
+/usr/bin/python3

+ 1 - 0
venv/bin/python3.12

@@ -0,0 +1 @@
+python3

二進制
venv/bin/uwsgi


+ 164 - 0
venv/include/site/python3.12/greenlet/greenlet.h

@@ -0,0 +1,164 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+
+/* Greenlet object interface */
+
+#ifndef Py_GREENLETOBJECT_H
+#define Py_GREENLETOBJECT_H
+
+
+#include <Python.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is deprecated and undocumented. It does not change. */
+#define GREENLET_VERSION "1.0.0"
+
+#ifndef GREENLET_MODULE
+#define implementation_ptr_t void*
+#endif
+
+typedef struct _greenlet {
+    PyObject_HEAD
+    PyObject* weakreflist;
+    PyObject* dict;
+    implementation_ptr_t pimpl;
+} PyGreenlet;
+
+#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
+
+
+/* C API functions */
+
+/* Total number of symbols that are exported */
+#define PyGreenlet_API_pointers 12
+
+#define PyGreenlet_Type_NUM 0
+#define PyExc_GreenletError_NUM 1
+#define PyExc_GreenletExit_NUM 2
+
+#define PyGreenlet_New_NUM 3
+#define PyGreenlet_GetCurrent_NUM 4
+#define PyGreenlet_Throw_NUM 5
+#define PyGreenlet_Switch_NUM 6
+#define PyGreenlet_SetParent_NUM 7
+
+#define PyGreenlet_MAIN_NUM 8
+#define PyGreenlet_STARTED_NUM 9
+#define PyGreenlet_ACTIVE_NUM 10
+#define PyGreenlet_GET_PARENT_NUM 11
+
+#ifndef GREENLET_MODULE
+/* This section is used by modules that uses the greenlet C API */
+static void** _PyGreenlet_API = NULL;
+
+#    define PyGreenlet_Type \
+        (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
+
+#    define PyExc_GreenletError \
+        ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
+
+#    define PyExc_GreenletExit \
+        ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
+
+/*
+ * PyGreenlet_New(PyObject *args)
+ *
+ * greenlet.greenlet(run, parent=None)
+ */
+#    define PyGreenlet_New                                        \
+        (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
+             _PyGreenlet_API[PyGreenlet_New_NUM])
+
+/*
+ * PyGreenlet_GetCurrent(void)
+ *
+ * greenlet.getcurrent()
+ */
+#    define PyGreenlet_GetCurrent \
+        (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
+
+/*
+ * PyGreenlet_Throw(
+ *         PyGreenlet *greenlet,
+ *         PyObject *typ,
+ *         PyObject *val,
+ *         PyObject *tb)
+ *
+ * g.throw(...)
+ */
+#    define PyGreenlet_Throw                 \
+        (*(PyObject * (*)(PyGreenlet * self, \
+                          PyObject * typ,    \
+                          PyObject * val,    \
+                          PyObject * tb))    \
+             _PyGreenlet_API[PyGreenlet_Throw_NUM])
+
+/*
+ * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
+ *
+ * g.switch(*args, **kwargs)
+ */
+#    define PyGreenlet_Switch                                              \
+        (*(PyObject *                                                      \
+           (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
+             _PyGreenlet_API[PyGreenlet_Switch_NUM])
+
+/*
+ * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
+ *
+ * g.parent = new_parent
+ */
+#    define PyGreenlet_SetParent                                 \
+        (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
+             _PyGreenlet_API[PyGreenlet_SetParent_NUM])
+
+/*
+ * PyGreenlet_GetParent(PyObject* greenlet)
+ *
+ * return greenlet.parent;
+ *
+ * This could return NULL even if there is no exception active.
+ * If it does not return NULL, you are responsible for decrementing the
+ * reference count.
+ */
+#     define PyGreenlet_GetParent                                    \
+    (*(PyGreenlet* (*)(PyGreenlet*))                                 \
+     _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
+
+/*
+ * deprecated, undocumented alias.
+ */
+#     define PyGreenlet_GET_PARENT PyGreenlet_GetParent
+
+#     define PyGreenlet_MAIN                                         \
+    (*(int (*)(PyGreenlet*))                                         \
+     _PyGreenlet_API[PyGreenlet_MAIN_NUM])
+
+#     define PyGreenlet_STARTED                                      \
+    (*(int (*)(PyGreenlet*))                                         \
+     _PyGreenlet_API[PyGreenlet_STARTED_NUM])
+
+#     define PyGreenlet_ACTIVE                                       \
+    (*(int (*)(PyGreenlet*))                                         \
+     _PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
+
+
+
+
+/* Macro that imports greenlet and initializes C API */
+/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
+   keep the older definition to be sure older code that might have a copy of
+   the header still works. */
+#    define PyGreenlet_Import()                                               \
+        {                                                                     \
+            _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
+        }
+
+#endif /* GREENLET_MODULE */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_GREENLETOBJECT_H */

+ 1 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/INSTALLER

@@ -0,0 +1 @@
+pip

+ 20 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Miguel Grinberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 91 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/METADATA

@@ -0,0 +1,91 @@
+Metadata-Version: 2.2
+Name: Flask-Migrate
+Version: 4.1.0
+Summary: SQLAlchemy database migrations for Flask applications using Alembic.
+Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
+License: MIT
+Project-URL: Homepage, https://github.com/miguelgrinberg/flask-migrate
+Project-URL: Bug Tracker, https://github.com/miguelgrinberg/flask-migrate/issues
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python :: 3
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Requires-Python: >=3.6
+Description-Content-Type: text/markdown
+License-File: LICENSE
+Requires-Dist: Flask>=0.9
+Requires-Dist: Flask-SQLAlchemy>=1.0
+Requires-Dist: alembic>=1.9.0
+Provides-Extra: dev
+Requires-Dist: tox; extra == "dev"
+Requires-Dist: flake8; extra == "dev"
+Requires-Dist: pytest; extra == "dev"
+Provides-Extra: docs
+Requires-Dist: sphinx; extra == "docs"
+
+Flask-Migrate
+=============
+
+[![Build status](https://github.com/miguelgrinberg/flask-migrate/workflows/build/badge.svg)](https://github.com/miguelgrinberg/flask-migrate/actions)
+
+Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command.
+
+Installation
+------------
+
+Install Flask-Migrate with `pip`:
+
+    pip install Flask-Migrate
+
+Example
+-------
+
+This is an example application that handles database migrations through Flask-Migrate:
+
+```python
+from flask import Flask
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+
+app = Flask(__name__)
+app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
+
+db = SQLAlchemy(app)
+migrate = Migrate(app, db)
+
+class User(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(128))
+```
+
+With the above application you can create the database or enable migrations if the database already exists with the following command:
+
+    $ flask db init
+
+Note that the `FLASK_APP` environment variable must be set according to the Flask documentation for this command to work. This will add a `migrations` folder to your application. The contents of this folder need to be added to version control along with your other source files. 
+
+You can then generate an initial migration:
+
+    $ flask db migrate
+    
+The migration script needs to be reviewed and edited, as Alembic currently does not detect every change you make to your models. In particular, Alembic is currently unable to detect indexes. Once finalized, the migration script also needs to be added to version control.
+
+Then you can apply the migration to the database:
+
+    $ flask db upgrade
+    
+Then each time the database models change repeat the `migrate` and `upgrade` commands.
+
+To sync the database in another system just refresh the `migrations` folder from source control and run the `upgrade` command.
+
+To see all the commands that are available run this command:
+
+    $ flask db --help
+
+Resources
+---------
+
+- [Documentation](http://flask-migrate.readthedocs.io/en/latest/)
+- [pypi](https://pypi.python.org/pypi/Flask-Migrate) 
+- [Change Log](https://github.com/miguelgrinberg/Flask-Migrate/blob/master/CHANGES.md)

+ 31 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/RECORD

@@ -0,0 +1,31 @@
+Flask_Migrate-4.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+Flask_Migrate-4.1.0.dist-info/LICENSE,sha256=kfkXGlJQvKy3Y__6tAJ8ynIp1HQfeROXhL8jZU1d-DI,1082
+Flask_Migrate-4.1.0.dist-info/METADATA,sha256=jifIy8PzfDzjuCEeKLDKRJA8O56KOgLfj3s2lmzZA-8,3289
+Flask_Migrate-4.1.0.dist-info/RECORD,,
+Flask_Migrate-4.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Flask_Migrate-4.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
+Flask_Migrate-4.1.0.dist-info/top_level.txt,sha256=jLoPgiMG6oR4ugNteXn3IHskVVIyIXVStZOVq-AWLdU,14
+flask_migrate/__init__.py,sha256=JMySGA55Y8Gxy3HviWu7qq5rPUNQBWc2NID2OicpDyw,10082
+flask_migrate/__pycache__/__init__.cpython-312.pyc,,
+flask_migrate/__pycache__/cli.cpython-312.pyc,,
+flask_migrate/cli.py,sha256=IxrxBSC82S5sPfWac8Qg83_FVsRvqTYtCG7HRyMW8RU,11097
+flask_migrate/templates/aioflask-multidb/README,sha256=Ek4cJqTaxneVjtkue--BXMlfpfp3MmJRjqoZvnSizww,43
+flask_migrate/templates/aioflask-multidb/__pycache__/env.cpython-312.pyc,,
+flask_migrate/templates/aioflask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
+flask_migrate/templates/aioflask-multidb/env.py,sha256=UcjeqkAbyUjTkuQFmCFPG7QOvqhco8-uGp8QEbto0T8,6573
+flask_migrate/templates/aioflask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246
+flask_migrate/templates/aioflask/README,sha256=KKqWGl4YC2RqdOdq-y6quTDW0b7D_UZNHuM8glM1L-c,44
+flask_migrate/templates/aioflask/__pycache__/env.cpython-312.pyc,,
+flask_migrate/templates/aioflask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
+flask_migrate/templates/aioflask/env.py,sha256=m6ZtBhdpwuq89vVeLTWmNT-1NfJZqarC_hsquCdR9bw,3478
+flask_migrate/templates/aioflask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494
+flask_migrate/templates/flask-multidb/README,sha256=AfiP5foaV2odZxXxuUuSIS6YhkIpR7CsOo2mpuxwHdc,40
+flask_migrate/templates/flask-multidb/__pycache__/env.cpython-312.pyc,,
+flask_migrate/templates/flask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
+flask_migrate/templates/flask-multidb/env.py,sha256=F44iqsAxLTVBN_zD8CMUkdE7Aub4niHMmo5wl9mY4Uw,6190
+flask_migrate/templates/flask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246
+flask_migrate/templates/flask/README,sha256=JL0NrjOrscPcKgRmQh1R3hlv1_rohDot0TvpmdM27Jk,41
+flask_migrate/templates/flask/__pycache__/env.cpython-312.pyc,,
+flask_migrate/templates/flask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
+flask_migrate/templates/flask/env.py,sha256=ibK1hsdOsOBzXNU2yQoAIza7f_EFzaVSWwON_NSpNzQ,3344
+flask_migrate/templates/flask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494

+ 0 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/REQUESTED


+ 5 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/WHEEL

@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (75.8.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+

+ 1 - 0
venv/lib/python3.12/site-packages/Flask_Migrate-4.1.0.dist-info/top_level.txt

@@ -0,0 +1 @@
+flask_migrate

+ 1 - 0
venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER

@@ -0,0 +1 @@
+pip

+ 28 - 0
venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt

@@ -0,0 +1,28 @@
+Copyright 2010 Pallets
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1.  Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+2.  Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+3.  Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 92 - 0
venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/METADATA

@@ -0,0 +1,92 @@
+Metadata-Version: 2.1
+Name: MarkupSafe
+Version: 3.0.2
+Summary: Safely add untrusted strings to HTML/XML markup.
+Maintainer-email: Pallets <contact@palletsprojects.com>
+License: Copyright 2010 Pallets
+        
+        Redistribution and use in source and binary forms, with or without
+        modification, are permitted provided that the following conditions are
+        met:
+        
+        1.  Redistributions of source code must retain the above copyright
+            notice, this list of conditions and the following disclaimer.
+        
+        2.  Redistributions in binary form must reproduce the above copyright
+            notice, this list of conditions and the following disclaimer in the
+            documentation and/or other materials provided with the distribution.
+        
+        3.  Neither the name of the copyright holder nor the names of its
+            contributors may be used to endorse or promote products derived from
+            this software without specific prior written permission.
+        
+        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+        "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+        HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+        SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+        TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+        PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+        LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+        SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+        
+Project-URL: Donate, https://palletsprojects.com/donate
+Project-URL: Documentation, https://markupsafe.palletsprojects.com/
+Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
+Project-URL: Source, https://github.com/pallets/markupsafe/
+Project-URL: Chat, https://discord.gg/pallets
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Text Processing :: Markup :: HTML
+Classifier: Typing :: Typed
+Requires-Python: >=3.9
+Description-Content-Type: text/markdown
+License-File: LICENSE.txt
+
+# MarkupSafe
+
+MarkupSafe implements a text object that escapes characters so it is
+safe to use in HTML and XML. Characters that have special meanings are
+replaced so that they display as the actual characters. This mitigates
+injection attacks, meaning untrusted user input can safely be displayed
+on a page.
+
+
+## Examples
+
+```pycon
+>>> from markupsafe import Markup, escape
+
+>>> # escape replaces special characters and wraps in Markup
+>>> escape("<script>alert(document.cookie);</script>")
+Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
+
+>>> # wrap in Markup to mark text "safe" and prevent escaping
+>>> Markup("<strong>Hello</strong>")
+Markup('<strong>hello</strong>')
+
+>>> escape(Markup("<strong>Hello</strong>"))
+Markup('<strong>hello</strong>')
+
+>>> # Markup is a str subclass
+>>> # methods and operators escape their arguments
+>>> template = Markup("Hello <em>{name}</em>")
+>>> template.format(name='"World"')
+Markup('Hello <em>&#34;World&#34;</em>')
+```
+
+## Donate
+
+The Pallets organization develops and supports MarkupSafe and other
+popular packages. In order to grow the community of contributors and
+users, and allow the maintainers to devote more time to the projects,
+[please donate today][].
+
+[please donate today]: https://palletsprojects.com/donate

Some files were not shown because too many files changed in this diff