From ba8e5ee95a0ba5e68edaa6324e3e3c9cf6635068 Mon Sep 17 00:00:00 2001 From: userName Date: Sun, 25 Aug 2024 04:20:21 +0000 Subject: [PATCH] lqs --- README.md | 9 ++ __init__.py | 0 __pycache__/__init__.cpython-312.pyc | Bin 0 -> 130 bytes __pycache__/main.cpython-312.pyc | Bin 0 -> 3341 bytes addons/__init__.py | 15 ++ addons/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 427 bytes addons/base/__init__.py | 1 + addons/base/__manifest__.py | 7 + .../base/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 176 bytes .../__pycache__/__manifest__.cpython-312.pyc | Bin 0 -> 212 bytes addons/base/models/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 193 bytes .../ir_module_module.cpython-312.pyc | Bin 0 -> 4477 bytes .../res_config_settings.cpython-312.pyc | Bin 0 -> 2524 bytes addons/base/models/ir_module_module.py | 86 +++++++++++ addons/base/models/res_config_settings.py | 28 ++++ database/__init__.py | 0 database/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 139 bytes database/__pycache__/database.cpython-312.pyc | Bin 0 -> 2343 bytes database/alembic.ini | 116 +++++++++++++++ database/alembic/README | 1 + .../alembic/__pycache__/env.cpython-312.pyc | Bin 0 -> 2854 bytes database/alembic/env.py | 83 +++++++++++ database/alembic/script.py.mako | 26 ++++ ...377a186d_initial_migration.cpython-312.pyc | Bin 0 -> 3394 bytes .../df08377a186d_initial_migration.py | 56 ++++++++ database/alembic_1.py | 12 ++ database/alembic_2.py | 19 +++ database/database.py | 37 +++++ main.py | 88 ++++++++++++ requirements.txt | 36 +++++ server.conf | 136 ++++++++++++++++++ server_1.py | 17 +++ server_2.py | 16 +++ 34 files changed, 790 insertions(+) create mode 100644 README.md create mode 100644 __init__.py create mode 100644 __pycache__/__init__.cpython-312.pyc create mode 100644 __pycache__/main.cpython-312.pyc create mode 100644 addons/__init__.py create mode 100644 addons/__pycache__/__init__.cpython-312.pyc create mode 100644 addons/base/__init__.py create mode 100644 addons/base/__manifest__.py create mode 100644 addons/base/__pycache__/__init__.cpython-312.pyc create mode 100644 addons/base/__pycache__/__manifest__.cpython-312.pyc create mode 100644 addons/base/models/__init__.py create mode 100644 addons/base/models/__pycache__/__init__.cpython-312.pyc create mode 100644 addons/base/models/__pycache__/ir_module_module.cpython-312.pyc create mode 100644 addons/base/models/__pycache__/res_config_settings.cpython-312.pyc create mode 100644 addons/base/models/ir_module_module.py create mode 100644 addons/base/models/res_config_settings.py create mode 100644 database/__init__.py create mode 100644 database/__pycache__/__init__.cpython-312.pyc create mode 100644 database/__pycache__/database.cpython-312.pyc create mode 100644 database/alembic.ini create mode 100644 database/alembic/README create mode 100644 database/alembic/__pycache__/env.cpython-312.pyc create mode 100644 database/alembic/env.py create mode 100644 database/alembic/script.py.mako create mode 100644 database/alembic/versions/__pycache__/df08377a186d_initial_migration.cpython-312.pyc create mode 100644 database/alembic/versions/df08377a186d_initial_migration.py create mode 100644 database/alembic_1.py create mode 100644 database/alembic_2.py create mode 100644 database/database.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 server.conf create mode 100644 server_1.py create mode 100644 server_2.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..005ed6f --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +alembic init alembic +[sqlalchemy] +url = postgresql+psycopg2://user:password@localhost/dbname + +import sys +import os.path +sys.path.append(os.path.realpath('../addons/base/models')) +import ir_module_module +target_metadata = ir_module_module.Base.metadata \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfad5b368394580d1623741620f90bd22d3aaa34 GIT binary patch literal 130 zcmX@j%ge<81o>Ncrh(|kAOanHW&w&!XQ*V*Wb|9fP{ah}eFmxdrKq2gpPQgjEsy$%s>_Z Dx3e5l literal 0 HcmV?d00001 diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a61e283cca4b93a029f3641f9a1e014f2d6e4d56 GIT binary patch literal 3341 zcmdTGTTC0-^^QIEjPU~#8?XTfmPdnWutVr}+f9Q+LfAw}QMAcc>hxg^o(qgKV|(um zX<$L}Hj$Qt%AtkIm3ObMy){g6)Qm3WA z3B2x-x-9&d_JZCmbz6AdhKSSyo^|3LN53S%xl4&EQSiU{0mPjxsTZi-wRe3=3)tER zv>5(|Bbl49c@ILRb>0us{lM9W`yI~zJqzGp%&vfH{0{qG_bU$eCI1Wi+ARA9fHR2) z>h{4ok^8|xPi!3MC+mFEoFg%Yelm*Ck0Dyppp=pZfiIdHI4ZBS3HAmNJ?qo zJ%!Us;(^QNH@LeepbD}_rExl^9Hly%ICS?skLFy-<6kkRBu|BL*)T`1UZTPnF5(sa?dS=`^4o$lRK!%nI{MMm0Bh^GzUSOnD_Vf z>)^CJjgv;wMLin6i8bn-Di9rl6#>~P2&-IFzj*uOFF(Bd!By2jh!q4adP(s^cpNZXMVVPm(x$ zhD#p`Fbm^t`0ZZ=a2aujg6y<5go`D?S_f#JqqI5nTldenBsYwM=nAKli{=BOj)Y4!UO3B2T zT1f^ByA*Z#1}4A|R&Z96iEOGjafVgme8hNNQ%(GGI-O3XO>CGcWpX^}rVa9}S;!W2 zT{WqX;ZxeIF+*T7p=_rmi>?I@#sRnBehNK4|q`Xmzyens~;gR=t$Z%Q~h) z@y7J5scJL;Q6SZ3GpO4z3H8|>sHu~r5!9`Es83M|^lgF6(Lhno-hhJ7SZcTo_1leV zpTkqLnpV|jQ4AKSF1G9h`?UfzJ&0hd zlV!{YV3?$;ht#}~B86G#0_wGPk{qT&b#hrjBdpm7OC@1*kwgK|CTFat9E6CB-XkkY zA#bEupRp$((<#*{v1)A)R=qR?O(<*djnL;(Ffs`%WGd>|l$l1_P;F~j)2JKlr7?TZ z)!wTi)1A}=t8z}V>Z(Q~8ME@WPN^$HDC zKc80$Sy{7MgNm8Vl&axOhI)%K!FiL0?RaKt{b(hbHdQlufX@C=kZ&?@ih&{aAnOFE z3@XgXhHOHohtzyln^kbePCNN7V?8{QpNZeIrAElBaLZ<(@dm(MH^*^*MQ7H~na3!! zgZg&Ri#w?MZ-{>@_-1eu#VRPaJXJ=qb=0@z4c=Bu?k8wu2b~AjC#dH!^6jA5=g9XM zHSdWoZg5wG{(h_V&DN#J?T)7|H`luFMy}AVg@q8;_!xzEQ11>pv4dU$(cK0V?kfeh zd@c7D)_lDs?^a{`2eY4gJ{wQ*!3dLbJXmA0=Wa6vtEwviMFQZ_;T#siOp!T5>2jLszhHbqoyqs zCF7!_8oC zCD{AH!o$lC&weJ9`!22pFO|l(gwWE>d$%@(u1BJOQ*5h&|$LkeRKLllH&jACM9NM*=^se$PM z(y1(|tgC@$fq)A`KSMH8IuOUoGBDIKq%vjkKxB}~EPiAjgk8h1ni0a9$kZdx0yLqP zA&RAvF`0=G%r0lpWO@lw;HSxPi@hK_y|g4V=N5Z@v0g!9NyaUv{Nh{OsTC!uc`5N= z0giYu9Up%S$jVF1O$D-8z%uNKNyQ+BCSwsN(7a-hwFbX5^fU5vQ}uHSiw*S?3kvj$ zQ;W({i}VvyQu6bP_2c6+^D;}~;@!0Ff%eT-e%wf zq7Q5gf+`c(uL~$&6i{B^u_EKLfcZC8W*(*o96TSG8Ccod-5cH8{Tux!xLjqC{KO2D VY2^6%i2+C#vjUBpr{u`O0sxJ*V^;tG literal 0 HcmV?d00001 diff --git a/addons/base/__init__.py b/addons/base/__init__.py new file mode 100644 index 0000000..9a7e03e --- /dev/null +++ b/addons/base/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/addons/base/__manifest__.py b/addons/base/__manifest__.py new file mode 100644 index 0000000..b0af5eb --- /dev/null +++ b/addons/base/__manifest__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Check Printing Base', + 'version': '1.0', +} diff --git a/addons/base/__pycache__/__init__.cpython-312.pyc b/addons/base/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5095a27a40290e0aeb47c7779cf770d84e6ad55f GIT binary patch literal 176 zcmX@j%ge<81m|w;OA`Uok3k$5V1hC}3ji6@8B!Qh7;_kM8KW2(87i4HnO`yjg*6#( zvE}Bcq~;X+X)@hnC}IYRtz`HN()mkQKO;XkRX?Y&*ib*Qpg_MkwWut$NIx+pB|ooN zKPj;|RX;vHGcU6wK3=b&@)w5<(4f+sRJ$S$pl*=i#URE9W=2NFdki8)Y(NeGPiram literal 0 HcmV?d00001 diff --git a/addons/base/__pycache__/__manifest__.cpython-312.pyc b/addons/base/__pycache__/__manifest__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28b02ca6fcd113bedb8f6bac8c2cbe0eb9bc2123 GIT binary patch literal 212 zcmX@j%ge<81h;PNOIrw}AA>kBzzk)4<^wXOGo&!2Fs3lgVPa&cWY%P<5_Zl=P0m&b zD9X$$$;?Yva7rvrtztIRGtgwZ#gdnpn|h1AEVZaOGe6I7B|{Ml(1_0<^L`oXXXNLm z>gN;|8|o((6zCVH76H}iC#Iz2=N0QG0Zq}5kIzla%S=lxE{TuVE2#X%VUwGmQks)$ lSHuo9ff0y{g@D8dW=2NF4_pkqN;mjLKC?12G8M4`g#kKNH_iY6 literal 0 HcmV?d00001 diff --git a/addons/base/models/__init__.py b/addons/base/models/__init__.py new file mode 100644 index 0000000..c32871d --- /dev/null +++ b/addons/base/models/__init__.py @@ -0,0 +1 @@ +from . import ir_module_module \ No newline at end of file diff --git a/addons/base/models/__pycache__/__init__.cpython-312.pyc b/addons/base/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a73750e90b3a4ebcf4108151d2bfd9f35e14574 GIT binary patch literal 193 zcmX@j%ge<81o}7krO5&5#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r6;%!kUb? z1Tu@_bMsS5b5fy{pC;2Sh9YL5>`I2uATxfM=x5~Trt0Su78~j(78K|grxul^7U?IZ zq~zxn>n9}^r|JVuNX;qMkB`sH%PfhH*DI*}#bE<6)~<*HXb8x*Vi4m4Gb1D8JqEcV HHXsK8rp7Q& literal 0 HcmV?d00001 diff --git a/addons/base/models/__pycache__/ir_module_module.cpython-312.pyc b/addons/base/models/__pycache__/ir_module_module.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1a313d01ca9214a762b4d4e1c0c86b2ffb3ffe4 GIT binary patch literal 4477 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 Cem`sg literal 0 HcmV?d00001 diff --git a/addons/base/models/ir_module_module.py b/addons/base/models/ir_module_module.py new file mode 100644 index 0000000..a65beac --- /dev/null +++ b/addons/base/models/ir_module_module.py @@ -0,0 +1,86 @@ +from sqlalchemy import Column, Integer, String, Enum +from sqlalchemy.ext.declarative import declarative_base +from enum import Enum as PythonEnum +from sqlalchemy.orm import Session +from fastapi import Depends, HTTPException, status, APIRouter +from server.database.database import get_db +import os +app = APIRouter() + +# 创建ORM基类 +Base = declarative_base() + +# 定义枚举类型 +class EnumState(PythonEnum): + installable = 'installable' + to_upgrade = 'to_upgrade' + to_remove = 'to_remove' + installed = 'installed' + uninstallable = 'uninstallable' + +# 定义SQLAlchemy模型 +class IrModuleModule(Base): + __tablename__ = "ir.module.module" + + id = Column(Integer, primary_key=True, index=True, doc="主键") + name = Column(String, doc="技术名称") + state = Column(Enum(*[member.value for member in EnumState], name='module_state'), nullable=False, server_default=EnumState.uninstallable.value) + +from pydantic import BaseModel, Field +from enum import Enum + +# 定义枚举类型 +class EnumState(Enum): + installable = 'installable' + to_upgrade = 'to_upgrade' + to_remove = 'to_remove' + installed = 'installed' + uninstallable = 'uninstallable' + uninstalled = 'uninstalled' + to_install = 'to_install' + +class PydanticIrModuleModule(BaseModel): + name: str | None = None + state: EnumState = EnumState.uninstallable # 设置默认值 + +@app.post("/create") +async def create_admin(admin: PydanticIrModuleModule, db: Session = Depends(get_db)): + new_admin = IrModuleModule( + name=admin.name, + ) + db.add(new_admin) + db.commit() + db.refresh(new_admin) + return {"message": "hr_admin created successfully"} + +@app.get("/search", response_model=list[PydanticIrModuleModule]) +async def search_all(db: Session = Depends(get_db)): + admin = db.query(IrModuleModule).all() + return admin + + +def process_addons_folder(folder_path): + addons_path = os.path.join(folder_path, 'addons') + + # 确保 addons 文件夹存在 + if not os.path.exists(addons_path): + print(f"Addons folder not found at {addons_path}") + return + + # 遍历 addons 文件夹中的所有文件夹 + for addon_dir in os.listdir(addons_path): + addon_path = os.path.join(addons_path, addon_dir) + + # 只处理文件夹 + if os.path.isdir(addon_path): + init_file_path = os.path.join(addon_path, '__init__.py') + + # 如果存在 __init__.py 文件 + if os.path.exists(init_file_path): + # 添加 import 语句 + with open(init_file_path, 'a') as f: + f.write('\nfrom . import ir_module_module\n') + + # 如果模块被标记为激活状态 + if check_module_activation(addon_dir): + create_table_in_db(addon_dir) \ No newline at end of file diff --git a/addons/base/models/res_config_settings.py b/addons/base/models/res_config_settings.py new file mode 100644 index 0000000..2fc85b0 --- /dev/null +++ b/addons/base/models/res_config_settings.py @@ -0,0 +1,28 @@ +from passlib.context import CryptContext +import configparser +import os +import logging +def get_password_hash(password): + pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + return pwd_context.hash(password) +# 初始化配置文件 +def read_conf(): + logger = logging.getLogger(__name__) + if os.path.exists("./server/server.conf"): + config_parser = configparser.ConfigParser() + config_parser.read("./server/server.conf") + with open("./server/server.conf", "r") as f: + lines = f.readlines() + comments = [line for line in lines if line.strip().startswith("#")] + config_parser.set("options", "admin_passwd", get_password_hash('admin')) + current_file_path = os.path.abspath(__file__) + current_directory = os.path.dirname(current_file_path) + addons_path = os.path.join(current_directory, 'addons,') + logfile_path = os.path.join(current_directory, 'database/server.log') + config_parser.set("options", "addons_path", addons_path) + config_parser.set("options", "log_level", ':INFO') + config_parser.set("options", "logfile", logfile_path) + with open("./server/server.conf", "w") as configfile: + configfile.writelines(comments) + config_parser.write(configfile) + logger.info("配置文件模块执行完毕") diff --git a/database/__init__.py b/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/database/__pycache__/__init__.cpython-312.pyc b/database/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e6adf4e237ee8d497458bc5df41ff35a141b491 GIT binary patch literal 139 zcmX@j%ge<81e@0EOasx6K?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^OG`f^KQ~oBr?A*i zKe3=dzc{t1EVW2KC9xziDX}F7(S=>w)gh7h0+gbi%t-u)@3v-PS7~B>M&7=S*HG=Kh~7q0~Gs#_udK^ zSgP4FnfQ@SM;9~0ek{5$g~Tjj%aXzG{ehGPykcgy(crHUS)##Z*?Vq3sF}pm^gZu+ z&-1?b-uL7A#bOZS(k16IJ)y%fV2F}4lk7>gMDh8UrUacqb&6*0~YG3Fwsl2qP6 zqJ`K&mJtWYa$?nV8%Qg0-aucO0#%SfOsWdm2)N+PiWOo7aeZeSWAfkc2CJc%Q;%r> z2nDJ|2gptRtk?ON!n``w5|&6vAwd%FjKqn`Dx^>GV%01YS&k&)vCt_Z!PzR3a5N-^ zl*kzpY!Aug*jY!tPbShCBIURGa3bEJa)}-V{A86A+k;&RS<#f9gp^Z~;G&w~swX7N zy$MOwuH&H?Q3bHdr=wIq%k1;{*6_YH{LUKo9riM+5K1bEa3U6qD5{a_9nqxRrLygj zxEL<2G7AS<_SKspS`db2R`RWfP$3BrBPKEC&OhLjL3m9L=G z_w=iVPND?GcBvXXo$#0Uf>=h+QfSfWy!cw`;EImX+ZW2LpYo4*;R-**4-1Q|@JY+L zmKkg9uk6-mD-1H4=*1=BAK4Aw=S_YKzRWhE&$fdyY4Z!rw7`J+LSYPmq={br7sLn@ z;QwWL3k`0NKn3_Jx7HKBo_Js%_!XuIN=Bcht!`sMTi-?9x@SLAhZ>NKdx7KHpjj{) z-jXX&9a1#5q<-6wv~>`_A}Tw8VLD4!(M|#>#B03kA#y7k!b%)Ra8*YRgKMkVWQ2bS0~Z(shG%=G;3-k4?ViW zT_k)eSZJCcjCGuZ=}=2Ga8H?8j)F*`6&w5-4vd<|^S^myc0NHm2$Kg@?wzciw9h!+p5qTJ zmOGxW=;3lreghtOUhr3-;cCC0xwRRzX}#WG#!U+x)hmqt{mit-(1$ zNhk_m6YG~~=V_Tx%hs*+2`MJg&Qd)kMj*iY61J0QGpS}uZjsKOkVFZLqoWv!cdAC1 zrqJLSKb14t}4U1Q|uGg Pfa_MRJa$Trkc|HT5*k4j literal 0 HcmV?d00001 diff --git a/database/alembic.ini b/database/alembic.ini new file mode 100644 index 0000000..44991fe --- /dev/null +++ b/database/alembic.ini @@ -0,0 +1,116 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = postgresql+psycopg2://fastapi:Sj89061189@localhost/fastapi + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[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 + +[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 diff --git a/database/alembic/README b/database/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/database/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/database/alembic/__pycache__/env.cpython-312.pyc b/database/alembic/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3f21240f2ad4682553a3e3135365b130b89943f GIT binary patch literal 2854 zcmZuzO=ufO6rR=YN~^UjDYE4xw(CyZ*os0T*`?`EC=GG?LufIfw1?;-*4nYW>S|Y+ z9ocbcz=1*%D6}~RN};_L=g>nhJto&)BJ3b%;~q*$p*I&N7<}rRS*@km9-*E0-n@Bl z^xk~m>W}_@9l`kS>F@JTG=%=R`j4?ON8wzpQ})_ud2!V29) zEW9zO7TxCt>eq! z7Kwu$LQFWthcg$iT*l1Ba}aLPuxy^8^GhxnwhiZZ!{Zmoby8_Eaw?BSdQ!KT$X2y@ zM2}IC-sLut(W_9a!LZw8jV9{^y^-{;*@j(NAob-!liFYSdc&Yb-DAr(@m0sD6Em;) zGA#KiX3#2OO0cbaZAUKPfV}r zZ?0V0RQlRVwxwh@5~U5~@yVLW>=CqK-o7pGzs(D%k3o^J>qXB{?0h;@*GP;C#n zd%Jp94?(kqLR^_r7uU5zkozFA+To3xzyY=XZiY|_5ql2ZlvpaX9`=^_T4I@S{ec{w z02raWi`RbN=*EA=b7-F%sG%othr&PI4EyzEWj~jEn+dpNzkONhaEyB6?qLMu9N?GB z4&Vz2!K)C*pq2|15P+EjmE#?N98$?1!5E(BRxGi`(d+>bwAEu7!@0Wfl=?q5pbH#Cn%?D%HQjLA4i}Ur13KW@XD5Bg*M9Xl&{S;ZTkuY z74IN^DuGnW{{o1h^az)DS0Q6xCnKjQdJJqHzoHn^0hs*kez=L!(J@#%&wYP@2cRv9 z1|~MOWLq0!LF%+UR__s+hEDFe}!Pqz|C&$i>Et@!9# zd~6Fv)rp@^3#1)yX~#E?p7`y;OF0tPUuYjI#?M$wf$+a_6t<2O)YCD;gPHayNNE2IUkTSCJPLOC_9(}3| RKT$^iQjR}SMraYv_&; None: + """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=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/database/alembic/script.py.mako b/database/alembic/script.py.mako new file mode 100644 index 0000000..fbc4b07 --- /dev/null +++ b/database/alembic/script.py.mako @@ -0,0 +1,26 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/database/alembic/versions/__pycache__/df08377a186d_initial_migration.cpython-312.pyc b/database/alembic/versions/__pycache__/df08377a186d_initial_migration.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82eea43b9a77667947f128b24b207d772ca03c17 GIT binary patch literal 3394 zcmdT{O-vg{6yCMR-t}UGA&?)UHi!VJNx+yuNGw!|5R#DalM<3BR;%se8DO3D+L>J^ zan!0rs#K0diJG893WpwCk%N!)=3}K^jIh&m+gsa1Z&5+2#HlmuwK0iNg4!NBl4s|= zH}l?`dEd-?{=wrB7}CD}>AUG_7sLEYiDCoRVEr31c*I;`AQNIB3vCJ;wS{a^d&tgG z+OB}8Bjhm0KyijR=m_!1g#_sACo3TcRUtcaK`?DQKxo3}EWVrakqx;hpUCZ&oVVnj z9po-k?uNpbAXE+Au!?+ZlECM=`{}SM>$0RYMdcYR>2gdJgb8#@*2t)7_)M?~PIsL= z-q$AuPM&}T3DSa1!XQSHj+)MpF4)xF)!oz4b+RMS-PCm|*wYj2?dc43^_~j!3c?@6 zQ^sdA_{->K0;yqSIIgHfxsNq$vG|zbE;nHC*s^VSVC=RkZjn=1Qp2;NB26JhGX#j@ zNQIghQ^V9ivC$CN$!}d@X$W-E7h1$t9BqARXr9}bEeZR!*&l_QC~)axapMuWHPwlJAHTjLNDgL&IT?NR$O#no^LcN>OBZ zN;6EdwnQd0giTg8Va0I_Cc?T#)$m2a@Rm#H+|2`oBje5}nXMq}BWgN<~0_`&_1kWAI7J9a!wGyJ7I!A1nEy5Q^TVKeP$<2p#OGZb_772M` z)m5}wwx*I!p-k@Hu5X)80Qq;;S@jQfR=?>7Yxr}+9TQ*@HH?L;fRS=G=91rh~* zhbUB9Q$yBpyNsN-Y{!*O`Tr8R+OmE8TWF~J4|MJ+)9Ec>p}a5M1hB(RsEn}sGK5(- z@vR7JfL>U;ON8wuYbYvrdK)UCavxdq1}aG=**}Ks$<{G_D-DPFo}VRo#F#aexo`tjS{Y*9mSQ;RY)*y;ta_^R*ET7 zW|A>%nq)W!V@e{b8r<;MQP7)gVbL=FrU`O^=l9!2g)9w_P7OiK=&EAnqV?S2+q3_j^k zCo*~tj1c~h;NU7a`1s7y7dg>*nm(VI%mdS=$!uAQqQH_hS#Qy{#ClTE(AP#=AWPZ24tGBurauk0+#{%Lcv z;IC?iJ^VYf_h)9`E1s!6G{?^$USr;W+1xhgnNKWSe%!KfeYNJ$8q@YN&}*?iSUSGg zk*d!&bgb5Nt}(qYJGs_t7<`d)?_`$JmX=2KF>-9?jq9 CwYl~H literal 0 HcmV?d00001 diff --git a/database/alembic/versions/df08377a186d_initial_migration.py b/database/alembic/versions/df08377a186d_initial_migration.py new file mode 100644 index 0000000..230abe2 --- /dev/null +++ b/database/alembic/versions/df08377a186d_initial_migration.py @@ -0,0 +1,56 @@ +"""Initial migration + +Revision ID: df08377a186d +Revises: +Create Date: 2024-08-12 09:44:54.105915 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'df08377a186d' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index('ix_admin_id', table_name='admin') + op.drop_index('ix_admin_name', table_name='admin') + op.drop_index('ix_admin_username', table_name='admin') + op.drop_table('admin') + op.drop_index('ix_products_id', table_name='products') + op.drop_index('ix_products_name', table_name='products') + op.drop_table('products') + op.drop_index('ix_ir.module.module_state', table_name='ir.module.module') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_index('ix_ir.module.module_state', 'ir.module.module', ['state'], unique=False) + op.create_table('products', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('described', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='products_pkey') + ) + op.create_index('ix_products_name', 'products', ['name'], unique=True) + op.create_index('ix_products_id', 'products', ['id'], unique=False) + op.create_table('admin', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('username', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('password', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('is_superuser', sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='admin_pkey') + ) + op.create_index('ix_admin_username', 'admin', ['username'], unique=True) + op.create_index('ix_admin_name', 'admin', ['name'], unique=False) + op.create_index('ix_admin_id', 'admin', ['id'], unique=False) + # ### end Alembic commands ### diff --git a/database/alembic_1.py b/database/alembic_1.py new file mode 100644 index 0000000..90f1991 --- /dev/null +++ b/database/alembic_1.py @@ -0,0 +1,12 @@ +import subprocess + +def run_service(): + bash_cmd = f""" + source /home/lqs1/app/venv/bin/activate && \ + cd /home/lqs1/app/server/database && \ + alembic revision -m "Initial migration" --autogenerate + """ + subprocess.Popen(bash_cmd, shell=True, executable='/bin/bash') + +if __name__ == '__main__': + run_service() diff --git a/database/alembic_2.py b/database/alembic_2.py new file mode 100644 index 0000000..0f5849a --- /dev/null +++ b/database/alembic_2.py @@ -0,0 +1,19 @@ +import subprocess + +def run_service(): + # 激活虚拟环境并运行服务 + # 注意:这里的命令被组合成了一个bash脚本字符串 + # 首先激活虚拟环境,然后改变目录,最后执行alembic命令 + bash_cmd = f""" + source /home/lqs1/app/venv/bin/activate && \ + cd /home/lqs1/app/server/database && \ + alembic upgrade head + """ + + # 使用 Popen 执行bash命令 + # 注意:这里使用shell=True,因为我们正在执行一个bash脚本 + # executable='/bin/bash' 是可选的,因为默认就是bash,但明确指出也无妨 + subprocess.Popen(bash_cmd, shell=True, executable='/bin/bash') + +if __name__ == '__main__': + run_service() diff --git a/database/database.py b/database/database.py new file mode 100644 index 0000000..2bb25e7 --- /dev/null +++ b/database/database.py @@ -0,0 +1,37 @@ +from sqlalchemy import create_engine, text +from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base +import configparser +import logging + +logger = logging.getLogger(__name__) +config_parser = configparser.ConfigParser() +config_parser.read("./server/server.conf") +host = config_parser.get('options', 'db_host') +port = config_parser.getint('options', 'db_port') +username = config_parser.get('options', 'db_user') +password = config_parser.get('options', 'db_password') +database = config_parser.get('options', 'db_name') +sqlname = config_parser.get('options', 'db_sqlname') +DATABASE_URL = f"{sqlname}://{username}:{password}@{host}:{port}/{database}" +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() +#获取数据库连接 +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + logger.info("数据库关闭") +# 检查数据库连接 +def check_database_connection(): + try: + with SessionLocal() as db: + db.execute(text("SELECT 1")) + logger.info("数据库连接测试执行完毕") + return True + except Exception as e: + logger.info(f"无法连接到数据库!错误: {e}") + return False \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..14f9525 --- /dev/null +++ b/main.py @@ -0,0 +1,88 @@ +from fastapi import FastAPI, Depends, HTTPException, status +import logging +from .database.database import engine +from .addons.base.models.ir_module_module import Base +from sqlalchemy.orm import Session +# 在应用启动时创建表 +Base.metadata.create_all(bind=engine) +app = FastAPI() +#临时测试导入 +from server.database.database import check_database_connection +from server.addons.base.models.res_config_settings import read_conf +from .database.database import get_db + +from importlib import import_module +import os +from pathlib import Path + +import os +import importlib.util + + + +def init_server_conf(): + logging.basicConfig(level=logging.DEBUG, format='%(message)s') + logging.info(f"fastapi执行完毕") + +@app.get("/") +async def root(): + read_conf() + check_database_connection() + init_server_conf() + +# app/main.py + +import pkgutil +import importlib +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base +from pathlib import Path + + + +# 数据库配置 +DATABASE_URL = "sqlite:///./test.db" +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + +# 自动加载模型 +def load_models(directory): + package_name = directory.name + for _, module_name, _ in pkgutil.iter_modules([str(directory)]): + full_module_name = f"{package_name}.{module_name}" + importlib.import_module(full_module_name) + +# 加载所有模型 +models_directory = Path(__file__).parent / "models" +load_models(models_directory) + +# 创建数据库表 +Base.metadata.create_all(bind=engine) + + +from fastapi import FastAPI +import pkgutil +import importlib +from pathlib import Path + + +# 自动加载模块 +def load_modules(directory): + package_name = directory.name + for _, module_name, _ in pkgutil.iter_modules([str(directory)]): + full_module_name = f"{package_name}.{module_name}" + importlib.import_module(full_module_name) + +# 加载所有模块 +modules_directory = Path(__file__).parent / "modules" +load_modules(modules_directory) + +# 添加路由 +for _, module_name, _ in pkgutil.iter_modules([str(modules_directory)]): + module = importlib.import_module(f"app.modules.{module_name}.routes") + if hasattr(module, "router"): + app.include_router(module.router) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a813377 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,36 @@ +# requirements.txt + +# pip install -r requirements.txt + +# server框架 +fastapi + +# 数据验证 +pydantic + +# 异步客户端 +asyncpg + +# SQLAlchemy支持ORM +sqlalchemy + +#http编码库 +python-multipart + +#server +uvicorn[standard] + +#pg数据库引擎 +psycopg2-binary + +#哈希算法验证 +passlib + +#加密 +pyJWT + +#数据库迁移 +alembic + +#配置文件的增删改查 +configparser \ No newline at end of file diff --git a/server.conf b/server.conf new file mode 100644 index 0000000..d2e5b99 --- /dev/null +++ b/server.conf @@ -0,0 +1,136 @@ +# addons_path: 指定 server 模块的路径,可以是多个路径,用逗号分隔。 +# admin_passwd: server 管理员的密码,这里使用了 passlib 库的哈希格式存储。 +# bin_path: 指定二进制文件的路径。 +# csv_internal_sep: CSV 文件内部字段分隔符,默认为空。 +# data_dir: 存储数据的目录,默认为用户的 .local/share/server 目录。 +# db_host: 数据库主机地址,默认为 localhost。 +# db_maxconn: 最大数据库连接数。 +# db_name: 默认使用的数据库名称。 +# db_password: 数据库密码。 +# db_port: 数据库端口,默认未设置则使用默认端口。 +# db_sslmode: 数据库 SSL 模式。 +# db_template: 数据库模板。 +# dbfilter: 数据库过滤器。 +# default_productivity_apps: 默认启用的生产力应用。 +# demo: 是否加载演示数据。 +# email_from: 发送邮件的发件人地址。 +# from_filter: 邮件过滤器。 +# geoip_database: GeoIP 数据库的位置。 +# gevent_port: gevent 服务端口。 +# http_enable: 是否启用 HTTP 服务。 +# http_interface: HTTP 服务监听的接口。 +# http_port: HTTP 服务监听的端口。 +# import_partial: 是否允许部分导入。 +# limit_memory_hard: 内存限制(硬)。 +# limit_memory_soft: 内存限制(软)。 +# limit_request: 单个请求的最大大小。 +# limit_time_cpu: 单个请求 CPU 时间限制。 +# limit_time_real: 单个请求实际时间限制。 +# limit_time_real_cron: cron 任务的实际时间限制。 +# list_db: 是否列出所有可用的数据库。 +# log_db: 是否记录数据库操作。 +# log_db_level: 数据库日志级别。 +# log_handler: 日志处理器。 +# log_level: 日志级别。 +# logfile: 日志文件路径。 +# max_cron_threads: 最大 cron 任务线程数。 +# osv_memory_age_limit: 对象存储的年龄限制。 +# osv_memory_count_limit: 对象存储的数量限制。 +# pg_path: PostgreSQL 客户端工具路径。 +# pidfile: 进程 ID 文件路径。 +# proxy_mode: 是否启用代理模式。 +# reportgz: 是否压缩报告。 +# screencasts: 是否记录屏幕录制。 +# screenshots: 是否记录屏幕截图。 +# server_wide_modules: 全局服务器模块。 +# smtp_password: SMTP 服务器密码。 +# smtp_port: SMTP 服务器端口。 +# smtp_server: SMTP 服务器地址。 +# smtp_ssl: 是否使用 SSL 连接 SMTP 服务器。 +# smtp_ssl_certificate_filename: SSL 证书文件名。 +# smtp_ssl_private_key_filename: SSL 私钥文件名。 +# smtp_user: SMTP 用户名。 +# syslog: 是否记录到系统日志。 +# test_enable: 是否启用测试模式。 +# test_file: 测试文件路径。 +# test_tags: 测试标签。 +# transient_age_limit: 短暂对象的年龄限制。 +# translate_modules: 要翻译的模块。 +# unaccent: 是否去除重音。 +# upgrade_path: 升级路径。 +# websocket_keep_alive_timeout: WebSocket 保持活动超时。 +# websocket_rate_limit_burst: WebSocket 速率限制突发次数。 +# websocket_rate_limit_delay: WebSocket 速率限制延迟。 +# without_demo: 是否不加载演示数据。 +# workers: 工作进程数量。 +# x_sendfile: 是否启用 X-Sendfile 支持。 +[options] +addons_path = /home/lqs1/app/server/addons/base/models/addons, +admin_passwd = $2b$12$nBrsS0ZDjpJwLRM.h7Vg8uyBaSPXjKeo7tpLzl1V1Do.bGvsJztMq +bin_path = +csv_internal_sep = +data_dir = +db_host = localhost +db_maxconn = +db_sqlname = postgresql +db_name = fastapi +db_user = fastapi +db_password = Sj89061189 +db_port = 5432 +db_sslmode = +db_template = +dbfilter = +default_productivity_apps = +demo = +email_from = +from_filter = +geoip_database = +gevent_port = +http_enable = +http_interface = +http_port = +import_partial = +limit_memory_hard = +limit_memory_soft = +limit_request = +limit_time_cpu = +limit_time_real = +limit_time_real_cron = +list_db = True +log_db = False +log_db_level = warning +log_handler = :INFO +log_level = :INFO +logfile = /home/lqs1/app/server/addons/base/models/database/server.log +max_cron_threads = +osv_memory_age_limit = +osv_memory_count_limit = +pg_path = +pidfile = +proxy_mode = +reportgz = +screencasts = +screenshots = +server_wide_modules = +smtp_password = +smtp_port = +smtp_server = +smtp_ssl = +smtp_ssl_certificate_filename = +smtp_ssl_private_key_filename = +smtp_user = +syslog = +test_enable = +test_file = +test_tags = +transient_age_limit = +translate_modules = +unaccent = +upgrade_path = +websocket_keep_alive_timeout = +websocket_rate_limit_burst = +websocket_rate_limit_delay = +without_demo = +workers = +x_sendfile = + diff --git a/server_1.py b/server_1.py new file mode 100644 index 0000000..c899ac7 --- /dev/null +++ b/server_1.py @@ -0,0 +1,17 @@ +# start_service.py +import subprocess + +def run_service(): + # 激活虚拟环境 + activate_cmd = '/home/lqs1/app/venv/bin/activate' + # 运行服务 + service_cmd = ['uvicorn', 'server.main:app', '--reload'] + + # 构建完整的命令 + full_cmd = f'source {activate_cmd} && cd /home/lqs1/app/ && uvicorn server.main:app --reload' + + # 使用 Popen 执行命令 + subprocess.Popen(full_cmd, shell=True, executable='/bin/bash') + +if __name__ == '__main__': + run_service() \ No newline at end of file diff --git a/server_2.py b/server_2.py new file mode 100644 index 0000000..cca0e70 --- /dev/null +++ b/server_2.py @@ -0,0 +1,16 @@ +# stop_service.py +import subprocess + +def stop_service(): + # 查找 uvicorn 进程 ID + find_cmd = "ps aux | grep 'uvicorn server.main:app --reload' | grep -v grep | awk '{print $2}'" + process = subprocess.run(find_cmd, shell=True, capture_output=True, text=True) + pid = process.stdout.strip() + + if pid: + # 终止进程 + kill_cmd = f"kill {pid}" + subprocess.run(kill_cmd, shell=True) + +if __name__ == '__main__': + stop_service() \ No newline at end of file