diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..49b9991 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +################ +# Stage: Build # +################ + +FROM python:3.13-slim AS build + +WORKDIR /app + +# Export poetry dependencies to file +RUN pip install --upgrade pip +COPY poetry.lock pyproject.toml ./ +RUN pip install poetry poetry-plugin-export +RUN poetry export --without-hashes --format requirements.txt --output /app/requirements.txt + +##################### +# Stage: Production # +##################### +FROM python:3.13-slim AS prod + +# ENV PYTHONPATH=/app + +WORKDIR /app + +# Copy requirements from build stage, and install them +COPY --from=build /app/requirements.txt . +RUN pip install --upgrade pip +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +# Create a non-root user to run the web server +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +# Run server +EXPOSE 8001 +# CMD ["gunicorn", "--bind", "0.0.0.0:8001", "main_package.app:app"] +CMD ["uvicorn", "main_package.app:app", "--reload", "--host", "0.0.0.0", "--port", "8001"] diff --git a/docker-build.sh b/docker-build.sh new file mode 100755 index 0000000..d8c8c37 --- /dev/null +++ b/docker-build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker-compose build + +echo "Finish!" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..24f0fea --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +services: + api: + build: . + image: app + ports: + - "8001:8001" + volumes: + - .:/app:ro + - ./datings.db:/app/datings.db:rw diff --git a/docker-down.sh b/docker-down.sh new file mode 100755 index 0000000..30a5bab --- /dev/null +++ b/docker-down.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker-compose down + +echo "Finish!" diff --git a/docker-up.sh b/docker-up.sh new file mode 100755 index 0000000..9e9eaf6 --- /dev/null +++ b/docker-up.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker-compose up + +echo "Finish!" diff --git a/LeraFoxQueen/main.py b/main_package/app.py similarity index 72% rename from LeraFoxQueen/main.py rename to main_package/app.py index b9c8b7a..d3283e2 100644 --- a/LeraFoxQueen/main.py +++ b/main_package/app.py @@ -5,9 +5,10 @@ import uuid from contextlib import asynccontextmanager from fastapi import FastAPI, Request, HTTPException -from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse +from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, FileResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from fastapi_sitemap import SiteMap, URLInfo from .database import create_tables, delete_tables from .router import router as datings_router @@ -44,6 +45,8 @@ app = FastAPI( lifespan=lifespan, docs_url=None, redoc_url=None, openapi_url=settings.OPENAPI_URL ) +favicon_path = "static/assets/img/favicons/favicon.ico" + templates = Jinja2Templates(directory="templates") app.include_router(datings_router) @@ -70,6 +73,11 @@ async def index(request: Request): return response +@app.get("/favicon.ico", include_in_schema=False) +async def favicon(): + return FileResponse(favicon_path) + + @app.get("/robots.txt", response_class=PlainTextResponse) def robots(): data = """User-agent: *\nAllow: /\nSitemap: /sitemap.xml""" @@ -81,5 +89,31 @@ async def http_exception_handler(request, exc): return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail}) +sitemap = SiteMap( + app=app, + base_url="https://lerafoxqueen.ru", + static_dirs=["static", "docs"], + exclude_patterns=[ + "^/api/", + "^/docs/", + "^/datings", + "^/api/", + "^/admin/", + ], # optional: exclude patterns + include_dynamic=True, + changefreq="daily", + priority_map={ + "/": 1.0, + }, + gzip=True, # optional: make a gz version too +) + +sitemap.attach() # now GET /sitemap.xml is live + +# @sitemap.source +# def extra_urls(): +# yield URLInfo("https://lerafoxqueen.ru/sitemap.xml") + + if __name__ == "__main__": - uvicorn.run(app, host="127.0.0.1", port=8001) + uvicorn.run(app, host="0.0.0.0", port=8001) diff --git a/LeraFoxQueen/database.py b/main_package/database.py similarity index 100% rename from LeraFoxQueen/database.py rename to main_package/database.py diff --git a/LeraFoxQueen/repository.py b/main_package/repository.py similarity index 100% rename from LeraFoxQueen/repository.py rename to main_package/repository.py diff --git a/LeraFoxQueen/router.py b/main_package/router.py similarity index 100% rename from LeraFoxQueen/router.py rename to main_package/router.py diff --git a/LeraFoxQueen/schemas.py b/main_package/schemas.py similarity index 100% rename from LeraFoxQueen/schemas.py rename to main_package/schemas.py diff --git a/LeraFoxQueen/validity.py b/main_package/validity.py similarity index 100% rename from LeraFoxQueen/validity.py rename to main_package/validity.py diff --git a/pyproject.toml b/pyproject.toml index 5e7a2d4..56f6859 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "LeraFoxQueen" +name = "main_package" version = "0.1.0" description = "" authors = [ @@ -13,6 +13,7 @@ dependencies = [ "hypercorn (>=0.17.3,<0.18.0)", "unicorn (>=2.1.3,<3.0.0)", "uvicorn (>=0.34.3,<0.35.0)", + "gunicorn (>=23.0.0,<24.0.0)", "fastapi (>=0.115.12,<0.116.0)", "aiosqlite (>=0.21.0,<0.22.0)", "sqlalchemy (>=2.0.41,<3.0.0)", @@ -29,8 +30,13 @@ dependencies = [ "sniffio (>=1.3.1,<2.0.0)", "jinja2 (>=3.1.6,<4.0.0)", "pydantic-settings (>=2.9.1,<3.0.0)", + "fastapi-sitemap (>=1.0.4,<2.0.0)", ] package-mode = false +packages = [{include = "main_package"}] + +[tool.poetry.requires-plugins] +poetry-plugin-export = ">=1.8" [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..3506b92 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,10 @@ + + + + https://lerafoxqueen.ru/ + 1.00 + + diff --git a/start.sh b/start.sh index 6d3c1ca..19fdbee 100755 --- a/start.sh +++ b/start.sh @@ -1,12 +1,22 @@ #!/bin/sh python -m venv .venv +source .venv/bin/activate +pip install -U pip +pip install uvicorn gunicorn hypercorn fastapi certifi poetry poetry-plugin-export +poetry self add poetry-plugin-export + +pip show uvicorn | grep -E "Name:|Version:|Location:" +pip show gunicorn | grep -E "Name:|Version:|Location:" +pip show fastapi | grep -E "Name:|Version:|Location:" + pyenv local 3.13 poetry env activate poetry lock poetry install --no-root -#hypercorn LeraFoxQueen.main:app --reload --bind 0.0.0.0:8001 -#poetry run hypercorn LeraFoxQueen/main:app --reload --bind 0.0.0.0:8001 -#uvicorn LeraFoxQueen.main:app --reload --host 0.0.0.0 --port 8001 -uvicorn LeraFoxQueen.main:app --reload --host 127.0.0.1 --port 8001 +#hypercorn main_package.app:app --reload -w $(nproc) --bind 0.0.0.0:8001 +#poetry run hypercorn main_package/app:app --reload -w $(nproc) --bind 0.0.0.0:8001 +#uvicorn main_package.app:app --reload --workers $(nproc) --host 0.0.0.0 --port 8001 +uvicorn main_package.app:app --reload --workers $(nproc) --host 127.0.0.1 --port 8001 +#gunicorn -b 0.0.0.0:8001 -k uvicorn.workers.UvicornWorker main_package.app:app diff --git a/static/node_modules/.yarn-integrity b/static/node_modules/.yarn-integrity index 0300614..e5c9390 100644 --- a/static/node_modules/.yarn-integrity +++ b/static/node_modules/.yarn-integrity @@ -1,5 +1,5 @@ { - "systemParams": "linux-x64-127", + "systemParams": "linux-x64-137", "modulesFolders": [ "node_modules" ], @@ -7,13 +7,11 @@ "linkedModules": [], "topLevelPatterns": [ "@fortawesome/fontawesome-free@^6.7.2", - "bootstrap-autocomplete@^2.3.7", "bootstrap@^5.3.6" ], "lockfileEntries": { "@fortawesome/fontawesome-free@^6.7.2": "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz#8249de9b7e22fcb3ceb5e66090c30a1d5492b81a", - "bootstrap-autocomplete@^2.3.7": "https://registry.yarnpkg.com/bootstrap-autocomplete/-/bootstrap-autocomplete-2.3.7.tgz#d3d0ebfe124f8b0299a99b1ecdf885816bbf29d8", - "bootstrap@^5.3.6": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.6.tgz#fbd91ebaff093f5b191a1c01a8c866d24f9fa6e1" + "bootstrap@^5.3.6": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.7.tgz#8640065036124d961d885d80b5945745e1154d90" }, "files": [], "artifacts": {} diff --git a/static/node_modules/bootstrap-autocomplete/LICENSE b/static/node_modules/bootstrap-autocomplete/LICENSE deleted file mode 100644 index 6b9973a..0000000 --- a/static/node_modules/bootstrap-autocomplete/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Paolo Casciello, contributors - -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. diff --git a/static/node_modules/bootstrap-autocomplete/README.md b/static/node_modules/bootstrap-autocomplete/README.md deleted file mode 100644 index cb34fe9..0000000 --- a/static/node_modules/bootstrap-autocomplete/README.md +++ /dev/null @@ -1,37 +0,0 @@ -Bootstrap Autocomplete -====================== - -![docs](https://readthedocs.org/projects/bootstrap-autocomplete/badge/?version=latest "Latest Docs") -[![Build Status](https://api.cirrus-ci.com/github/xcash/bootstrap-autocomplete.svg)](https://cirrus-ci.com/github/xcash/bootstrap-autocomplete) - -Autocomplete plugin for Bootstrap 4.x and 3.x. - -It enhances form `input` and `select` field to provide autocomplete/typeahead capabilities. - -[Documentation](http://bootstrap-autocomplete.rtfd.io/) - -Latest version: 2.3.7 (2020/08/27) - -Version 2.0.0 and up supports Boostrap v4.x and old v3.x out of the box. - -Try the DEMO! -============= - -[Bootstrap 4 latest version](https://raw.githack.com/xcash/bootstrap-autocomplete/master/dist/latest/index.html) - -[Bootstrap 3 latest version](https://raw.githack.com/xcash/bootstrap-autocomplete/master/dist/latest/indexV3.html) - - -Creating DEV Environment -======================== - - docker-compose build --pull - -The *first time* install all dependencies with yarn - - docker-compose run --rm tools yarn install - -Running DEV Environment -======================= - - docker-compose up diff --git a/static/node_modules/bootstrap-autocomplete/dist/latest/bootstrap-autocomplete.js b/static/node_modules/bootstrap-autocomplete/dist/latest/bootstrap-autocomplete.js deleted file mode 100644 index 52a2c06..0000000 --- a/static/node_modules/bootstrap-autocomplete/dist/latest/bootstrap-autocomplete.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){var e={};function s(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,s),o.l=!0,o.exports}s.m=t,s.c=e,s.d=function(t,e,i){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(s.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)s.d(i,o,function(e){return t[e]}.bind(null,o));return i},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){"use strict";s.r(e),s.d(e,"AutoComplete",(function(){return a}));var i,o=(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var s in e)e.hasOwnProperty(s)&&(t[s]=e[s])})(t,e)},function(t,e){function s(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(s.prototype=e.prototype,new s)}),n=function(t){function e(e){return t.call(this,e)||this}return o(e,t),e.prototype.getDefaults=function(){return{url:"",method:"get",queryKey:"q",extraData:{},timeout:void 0,requestThrottling:500}},e.prototype.search=function(t,e){var s=this;null!=this.jqXHR&&this.jqXHR.abort();var i={};i[this._settings.queryKey]=t,$.extend(i,this._settings.extraData),this.requestTID&&window.clearTimeout(this.requestTID),this.requestTID=window.setTimeout((function(){s.jqXHR=$.ajax(s._settings.url,{method:s._settings.method,data:i,timeout:s._settings.timeout}),s.jqXHR.done((function(t){e(t)})),s.jqXHR.fail((function(t){var e;null===(e=s._settings)||void 0===e||e.fail(t)})),s.jqXHR.always((function(){s.jqXHR=null}))}),this._settings.requestThrottling)},e}(function(){function t(t){this._settings=$.extend(!0,{},this.getDefaults(),t)}return t.prototype.getDefaults=function(){return{}},t.prototype.getResults=function(t,e,s){return this.results},t.prototype.search=function(t,e){e(this.getResults())},t}()),r=function(){function t(t,e,s,i){this.initialized=!1,this.shown=!1,this.items=[],this.ddMouseover=!1,this._$el=t,this.formatItem=e,this.autoSelect=s,this.noResultsText=i}return t.prototype.init=function(){var t=this,e=$.extend({},this._$el.position(),{height:this._$el[0].offsetHeight});this._dd=$("