aury-boot 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aury/boot/__init__.py +66 -0
- aury/boot/_version.py +2 -2
- aury/boot/application/__init__.py +120 -0
- aury/boot/application/app/__init__.py +39 -0
- aury/boot/application/app/base.py +511 -0
- aury/boot/application/app/components.py +434 -0
- aury/boot/application/app/middlewares.py +101 -0
- aury/boot/application/config/__init__.py +44 -0
- aury/boot/application/config/settings.py +663 -0
- aury/boot/application/constants/__init__.py +19 -0
- aury/boot/application/constants/components.py +50 -0
- aury/boot/application/constants/scheduler.py +28 -0
- aury/boot/application/constants/service.py +29 -0
- aury/boot/application/errors/__init__.py +55 -0
- aury/boot/application/errors/chain.py +80 -0
- aury/boot/application/errors/codes.py +67 -0
- aury/boot/application/errors/exceptions.py +238 -0
- aury/boot/application/errors/handlers.py +320 -0
- aury/boot/application/errors/response.py +120 -0
- aury/boot/application/interfaces/__init__.py +76 -0
- aury/boot/application/interfaces/egress.py +224 -0
- aury/boot/application/interfaces/ingress.py +98 -0
- aury/boot/application/middleware/__init__.py +22 -0
- aury/boot/application/middleware/logging.py +451 -0
- aury/boot/application/migrations/__init__.py +13 -0
- aury/boot/application/migrations/manager.py +685 -0
- aury/boot/application/migrations/setup.py +237 -0
- aury/boot/application/rpc/__init__.py +63 -0
- aury/boot/application/rpc/base.py +108 -0
- aury/boot/application/rpc/client.py +294 -0
- aury/boot/application/rpc/discovery.py +218 -0
- aury/boot/application/scheduler/__init__.py +13 -0
- aury/boot/application/scheduler/runner.py +123 -0
- aury/boot/application/server/__init__.py +296 -0
- aury/boot/commands/__init__.py +30 -0
- aury/boot/commands/add.py +76 -0
- aury/boot/commands/app.py +105 -0
- aury/boot/commands/config.py +177 -0
- aury/boot/commands/docker.py +367 -0
- aury/boot/commands/docs.py +284 -0
- aury/boot/commands/generate.py +1277 -0
- aury/boot/commands/init.py +892 -0
- aury/boot/commands/migrate/__init__.py +37 -0
- aury/boot/commands/migrate/app.py +54 -0
- aury/boot/commands/migrate/commands.py +303 -0
- aury/boot/commands/scheduler.py +124 -0
- aury/boot/commands/server/__init__.py +21 -0
- aury/boot/commands/server/app.py +541 -0
- aury/boot/commands/templates/generate/api.py.tpl +105 -0
- aury/boot/commands/templates/generate/model.py.tpl +17 -0
- aury/boot/commands/templates/generate/repository.py.tpl +19 -0
- aury/boot/commands/templates/generate/schema.py.tpl +29 -0
- aury/boot/commands/templates/generate/service.py.tpl +48 -0
- aury/boot/commands/templates/project/CLI.md.tpl +92 -0
- aury/boot/commands/templates/project/DEVELOPMENT.md.tpl +1397 -0
- aury/boot/commands/templates/project/README.md.tpl +111 -0
- aury/boot/commands/templates/project/admin_console_init.py.tpl +50 -0
- aury/boot/commands/templates/project/config.py.tpl +30 -0
- aury/boot/commands/templates/project/conftest.py.tpl +26 -0
- aury/boot/commands/templates/project/env.example.tpl +213 -0
- aury/boot/commands/templates/project/gitignore.tpl +128 -0
- aury/boot/commands/templates/project/main.py.tpl +41 -0
- aury/boot/commands/templates/project/modules/api.py.tpl +19 -0
- aury/boot/commands/templates/project/modules/exceptions.py.tpl +84 -0
- aury/boot/commands/templates/project/modules/schedules.py.tpl +18 -0
- aury/boot/commands/templates/project/modules/tasks.py.tpl +20 -0
- aury/boot/commands/worker.py +143 -0
- aury/boot/common/__init__.py +35 -0
- aury/boot/common/exceptions/__init__.py +114 -0
- aury/boot/common/i18n/__init__.py +16 -0
- aury/boot/common/i18n/translator.py +272 -0
- aury/boot/common/logging/__init__.py +716 -0
- aury/boot/contrib/__init__.py +10 -0
- aury/boot/contrib/admin_console/__init__.py +18 -0
- aury/boot/contrib/admin_console/auth.py +137 -0
- aury/boot/contrib/admin_console/discovery.py +69 -0
- aury/boot/contrib/admin_console/install.py +172 -0
- aury/boot/contrib/admin_console/utils.py +44 -0
- aury/boot/domain/__init__.py +79 -0
- aury/boot/domain/exceptions/__init__.py +132 -0
- aury/boot/domain/models/__init__.py +51 -0
- aury/boot/domain/models/base.py +69 -0
- aury/boot/domain/models/mixins.py +135 -0
- aury/boot/domain/models/models.py +96 -0
- aury/boot/domain/pagination/__init__.py +279 -0
- aury/boot/domain/repository/__init__.py +23 -0
- aury/boot/domain/repository/impl.py +423 -0
- aury/boot/domain/repository/interceptors.py +47 -0
- aury/boot/domain/repository/interface.py +106 -0
- aury/boot/domain/repository/query_builder.py +348 -0
- aury/boot/domain/service/__init__.py +11 -0
- aury/boot/domain/service/base.py +73 -0
- aury/boot/domain/transaction/__init__.py +404 -0
- aury/boot/infrastructure/__init__.py +104 -0
- aury/boot/infrastructure/cache/__init__.py +31 -0
- aury/boot/infrastructure/cache/backends.py +348 -0
- aury/boot/infrastructure/cache/base.py +68 -0
- aury/boot/infrastructure/cache/exceptions.py +37 -0
- aury/boot/infrastructure/cache/factory.py +94 -0
- aury/boot/infrastructure/cache/manager.py +274 -0
- aury/boot/infrastructure/database/__init__.py +39 -0
- aury/boot/infrastructure/database/config.py +71 -0
- aury/boot/infrastructure/database/exceptions.py +44 -0
- aury/boot/infrastructure/database/manager.py +317 -0
- aury/boot/infrastructure/database/query_tools/__init__.py +164 -0
- aury/boot/infrastructure/database/strategies/__init__.py +198 -0
- aury/boot/infrastructure/di/__init__.py +15 -0
- aury/boot/infrastructure/di/container.py +393 -0
- aury/boot/infrastructure/events/__init__.py +33 -0
- aury/boot/infrastructure/events/bus.py +362 -0
- aury/boot/infrastructure/events/config.py +52 -0
- aury/boot/infrastructure/events/consumer.py +134 -0
- aury/boot/infrastructure/events/middleware.py +51 -0
- aury/boot/infrastructure/events/models.py +63 -0
- aury/boot/infrastructure/monitoring/__init__.py +529 -0
- aury/boot/infrastructure/scheduler/__init__.py +19 -0
- aury/boot/infrastructure/scheduler/exceptions.py +37 -0
- aury/boot/infrastructure/scheduler/manager.py +478 -0
- aury/boot/infrastructure/storage/__init__.py +38 -0
- aury/boot/infrastructure/storage/base.py +164 -0
- aury/boot/infrastructure/storage/exceptions.py +37 -0
- aury/boot/infrastructure/storage/factory.py +88 -0
- aury/boot/infrastructure/tasks/__init__.py +24 -0
- aury/boot/infrastructure/tasks/config.py +45 -0
- aury/boot/infrastructure/tasks/constants.py +37 -0
- aury/boot/infrastructure/tasks/exceptions.py +37 -0
- aury/boot/infrastructure/tasks/manager.py +490 -0
- aury/boot/testing/__init__.py +24 -0
- aury/boot/testing/base.py +122 -0
- aury/boot/testing/client.py +163 -0
- aury/boot/testing/factory.py +154 -0
- aury/boot/toolkit/__init__.py +21 -0
- aury/boot/toolkit/http/__init__.py +367 -0
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.4.dist-info}/METADATA +3 -2
- aury_boot-0.0.4.dist-info/RECORD +137 -0
- aury_boot-0.0.2.dist-info/RECORD +0 -5
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.4.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""迁移命令行工具。
|
|
2
|
+
|
|
3
|
+
提供数据库迁移管理的命令行接口。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .app import app, get_manager
|
|
7
|
+
from .commands import (
|
|
8
|
+
check,
|
|
9
|
+
down,
|
|
10
|
+
history,
|
|
11
|
+
make,
|
|
12
|
+
merge,
|
|
13
|
+
show,
|
|
14
|
+
status,
|
|
15
|
+
up,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main() -> None:
|
|
20
|
+
"""命令行入口函数。"""
|
|
21
|
+
app()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"app",
|
|
26
|
+
"check",
|
|
27
|
+
"down",
|
|
28
|
+
"get_manager",
|
|
29
|
+
"history",
|
|
30
|
+
"main",
|
|
31
|
+
"make",
|
|
32
|
+
"merge",
|
|
33
|
+
"show",
|
|
34
|
+
"status",
|
|
35
|
+
"up",
|
|
36
|
+
]
|
|
37
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""迁移 CLI 应用定义。
|
|
2
|
+
|
|
3
|
+
定义 Typer 应用和辅助函数。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from aury.boot.application.config import BaseConfig
|
|
11
|
+
from aury.boot.application.migrations import MigrationManager
|
|
12
|
+
|
|
13
|
+
# 创建 Typer 应用
|
|
14
|
+
app = typer.Typer(
|
|
15
|
+
name="migrate",
|
|
16
|
+
help="数据库迁移管理工具(类似 Django 的 migrate 命令)",
|
|
17
|
+
add_completion=False,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_manager(
|
|
22
|
+
config_override: str | None = None,
|
|
23
|
+
) -> MigrationManager:
|
|
24
|
+
"""获取迁移管理器。
|
|
25
|
+
|
|
26
|
+
从应用配置中获取所有必要参数并创建迁移管理器。
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
config_override: 覆盖配置文件路径(可选)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
MigrationManager: 迁移管理器实例
|
|
33
|
+
"""
|
|
34
|
+
# 加载应用配置
|
|
35
|
+
app_config = BaseConfig()
|
|
36
|
+
|
|
37
|
+
# 从配置中提取参数
|
|
38
|
+
migration_settings = app_config.migration
|
|
39
|
+
|
|
40
|
+
# 创建迁移管理器(将配置转换为参数传递)
|
|
41
|
+
return MigrationManager(
|
|
42
|
+
database_url=app_config.database.url,
|
|
43
|
+
config_path=config_override or migration_settings.config_path,
|
|
44
|
+
script_location=migration_settings.script_location,
|
|
45
|
+
model_modules=migration_settings.model_modules,
|
|
46
|
+
auto_create=migration_settings.auto_create,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"app",
|
|
52
|
+
"get_manager",
|
|
53
|
+
]
|
|
54
|
+
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""迁移命令实现。
|
|
2
|
+
|
|
3
|
+
实现所有迁移相关的命令行命令。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import traceback
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
import typer
|
|
14
|
+
|
|
15
|
+
from .app import app, get_manager
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _handle_exception(e: Exception, operation: str) -> None:
|
|
21
|
+
"""统一处理异常,打印完整堆栈信息。
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
e: 异常对象
|
|
25
|
+
operation: 操作名称(用于错误消息)
|
|
26
|
+
"""
|
|
27
|
+
typer.echo(f"\n❌ {operation}失败: {e}", err=True)
|
|
28
|
+
typer.echo("\n📋 异常堆栈:", err=True)
|
|
29
|
+
typer.echo(traceback.format_exc(), err=True)
|
|
30
|
+
raise typer.Exit(1)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.command()
|
|
34
|
+
def make(
|
|
35
|
+
message: str = typer.Option(..., "-m", "--message", help="迁移消息"),
|
|
36
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
37
|
+
autogenerate: bool = typer.Option(True, "--autogenerate/--no-autogenerate", help="是否自动生成"),
|
|
38
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="干运行(只检测变更,不生成文件)"),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""生成迁移文件(类似 Django 的 makemigrations)。
|
|
41
|
+
|
|
42
|
+
示例:
|
|
43
|
+
aury migrate make -m "add user table"
|
|
44
|
+
aury migrate make -m "update schema" --no-autogenerate
|
|
45
|
+
aury migrate make -m "check changes" --dry-run
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
manager = get_manager(config_override=config)
|
|
49
|
+
|
|
50
|
+
async def _make():
|
|
51
|
+
result = await manager.make_migrations(
|
|
52
|
+
message=message,
|
|
53
|
+
autogenerate=autogenerate,
|
|
54
|
+
dry_run=dry_run,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if dry_run:
|
|
58
|
+
changes = result.get("changes", [])
|
|
59
|
+
if changes:
|
|
60
|
+
typer.echo(f"\n📝 检测到 {len(changes)} 个变更:")
|
|
61
|
+
for change in changes:
|
|
62
|
+
typer.echo(f" - {change['type']}: {change['description']}")
|
|
63
|
+
else:
|
|
64
|
+
typer.echo("✅ 没有检测到模型变更")
|
|
65
|
+
else:
|
|
66
|
+
typer.echo(f"✅ 迁移文件已生成: {result.get('path', '')}")
|
|
67
|
+
changes = result.get("changes", [])
|
|
68
|
+
if changes:
|
|
69
|
+
typer.echo(f"📝 包含 {len(changes)} 个变更")
|
|
70
|
+
|
|
71
|
+
asyncio.run(_make())
|
|
72
|
+
except Exception as e:
|
|
73
|
+
_handle_exception(e, "生成迁移")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@app.command()
|
|
77
|
+
def up(
|
|
78
|
+
revision: str = typer.Option("head", "-r", "--revision", help="目标版本(默认 head)"),
|
|
79
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
80
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="干运行(只显示会执行的迁移,不实际执行)"),
|
|
81
|
+
) -> None:
|
|
82
|
+
"""执行迁移(类似 Django 的 migrate)。
|
|
83
|
+
|
|
84
|
+
示例:
|
|
85
|
+
aury migrate up
|
|
86
|
+
aury migrate up -r "abc123"
|
|
87
|
+
aury migrate up --dry-run
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
manager = get_manager(config_override=config)
|
|
91
|
+
|
|
92
|
+
async def _upgrade():
|
|
93
|
+
await manager.upgrade(revision=revision, dry_run=dry_run)
|
|
94
|
+
if not dry_run:
|
|
95
|
+
typer.echo(f"✅ 迁移已执行到版本: {revision}")
|
|
96
|
+
|
|
97
|
+
asyncio.run(_upgrade())
|
|
98
|
+
except Exception as e:
|
|
99
|
+
_handle_exception(e, "执行迁移")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command()
|
|
103
|
+
def down(
|
|
104
|
+
revision: str = typer.Argument(..., help="目标版本(如 previous, -1, 或具体版本号)"),
|
|
105
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
106
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="干运行(只显示会回滚的迁移,不实际执行)"),
|
|
107
|
+
) -> None:
|
|
108
|
+
"""回滚迁移。
|
|
109
|
+
|
|
110
|
+
示例:
|
|
111
|
+
aury migrate down previous
|
|
112
|
+
aury migrate down -1
|
|
113
|
+
aury migrate down abc123 --dry-run
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
manager = get_manager(config_override=config)
|
|
117
|
+
|
|
118
|
+
async def _downgrade():
|
|
119
|
+
await manager.downgrade(revision=revision, dry_run=dry_run)
|
|
120
|
+
if not dry_run:
|
|
121
|
+
typer.echo(f"✅ 迁移已回滚到版本: {revision}")
|
|
122
|
+
|
|
123
|
+
asyncio.run(_downgrade())
|
|
124
|
+
except Exception as e:
|
|
125
|
+
_handle_exception(e, "回滚迁移")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@app.command()
|
|
129
|
+
def status(
|
|
130
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
131
|
+
) -> None:
|
|
132
|
+
"""查看迁移状态(类似 Django 的 showmigrations)。
|
|
133
|
+
|
|
134
|
+
示例:
|
|
135
|
+
aury migrate status
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
manager = get_manager(config_override=config)
|
|
139
|
+
|
|
140
|
+
async def _status():
|
|
141
|
+
status_info = await manager.status()
|
|
142
|
+
|
|
143
|
+
# 格式化输出
|
|
144
|
+
typer.echo("\n📊 迁移状态:")
|
|
145
|
+
typer.echo(f" 当前版本: {status_info.get('current', 'None')}")
|
|
146
|
+
typer.echo(f" 最新版本: {status_info.get('head', 'None')}")
|
|
147
|
+
|
|
148
|
+
pending = status_info.get('pending', [])
|
|
149
|
+
applied = status_info.get('applied', [])
|
|
150
|
+
|
|
151
|
+
if pending:
|
|
152
|
+
typer.echo(f"\n⏳ 待执行迁移 ({len(pending)}):")
|
|
153
|
+
for rev in pending:
|
|
154
|
+
typer.echo(f" - {rev}")
|
|
155
|
+
else:
|
|
156
|
+
typer.echo("\n✅ 所有迁移已执行")
|
|
157
|
+
|
|
158
|
+
if applied:
|
|
159
|
+
typer.echo(f"\n✅ 已执行迁移 ({len(applied)}):")
|
|
160
|
+
for rev in applied:
|
|
161
|
+
typer.echo(f" - {rev}")
|
|
162
|
+
|
|
163
|
+
asyncio.run(_status())
|
|
164
|
+
except Exception as e:
|
|
165
|
+
_handle_exception(e, "查看状态")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@app.command()
|
|
169
|
+
def show(
|
|
170
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
171
|
+
) -> None:
|
|
172
|
+
"""显示所有迁移(类似 Django 的 showmigrations)。
|
|
173
|
+
|
|
174
|
+
示例:
|
|
175
|
+
aury migrate show
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
manager = get_manager(config_override=config)
|
|
179
|
+
|
|
180
|
+
async def _show():
|
|
181
|
+
migrations = await manager.show()
|
|
182
|
+
|
|
183
|
+
if not migrations:
|
|
184
|
+
typer.echo("📝 没有找到迁移文件")
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
# 使用 Rich 表格显示
|
|
188
|
+
table = Table(title="📝 所有迁移", show_header=True, header_style="bold magenta")
|
|
189
|
+
table.add_column("版本", style="cyan", width=15)
|
|
190
|
+
table.add_column("父版本", style="yellow", width=15)
|
|
191
|
+
table.add_column("消息", style="green")
|
|
192
|
+
|
|
193
|
+
for mig in migrations:
|
|
194
|
+
revision = mig.get('revision', '')[:12]
|
|
195
|
+
down_revision = mig.get('down_revision', '')[:12] if mig.get('down_revision') else '-'
|
|
196
|
+
message = mig.get('message', '')
|
|
197
|
+
table.add_row(revision, down_revision, message)
|
|
198
|
+
|
|
199
|
+
console.print(table)
|
|
200
|
+
|
|
201
|
+
asyncio.run(_show())
|
|
202
|
+
except Exception as e:
|
|
203
|
+
_handle_exception(e, "显示迁移")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@app.command()
|
|
207
|
+
def check(
|
|
208
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
209
|
+
) -> None:
|
|
210
|
+
"""检查迁移(类似 Django 的 check)。
|
|
211
|
+
|
|
212
|
+
检查迁移文件是否有问题。
|
|
213
|
+
|
|
214
|
+
示例:
|
|
215
|
+
aury migrate check
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
manager = get_manager(config_override=config)
|
|
219
|
+
|
|
220
|
+
async def _check():
|
|
221
|
+
result = await manager.check()
|
|
222
|
+
|
|
223
|
+
if result["valid"]:
|
|
224
|
+
typer.echo("✅ 迁移检查通过")
|
|
225
|
+
else:
|
|
226
|
+
typer.echo("❌ 发现迁移问题:")
|
|
227
|
+
for issue in result["issues"]:
|
|
228
|
+
typer.echo(f" - {issue}")
|
|
229
|
+
|
|
230
|
+
if result["warnings"]:
|
|
231
|
+
typer.echo("\n⚠️ 警告:")
|
|
232
|
+
for warning in result["warnings"]:
|
|
233
|
+
typer.echo(f" - {warning}")
|
|
234
|
+
|
|
235
|
+
typer.echo("\n📊 统计:")
|
|
236
|
+
typer.echo(f" 迁移总数: {result['revision_count']}")
|
|
237
|
+
typer.echo(f" Head 数量: {result['head_count']}")
|
|
238
|
+
|
|
239
|
+
asyncio.run(_check())
|
|
240
|
+
except Exception as e:
|
|
241
|
+
_handle_exception(e, "检查")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@app.command()
|
|
245
|
+
def merge(
|
|
246
|
+
revisions: str = typer.Argument(..., help="要合并的版本(逗号分隔)"),
|
|
247
|
+
message: str | None = typer.Option(None, "-m", "--message", help="合并消息"),
|
|
248
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
249
|
+
) -> None:
|
|
250
|
+
"""合并迁移(类似 Django 的迁移合并)。
|
|
251
|
+
|
|
252
|
+
当有多个分支时,创建合并迁移。
|
|
253
|
+
|
|
254
|
+
示例:
|
|
255
|
+
aury migrate merge "abc123,def456"
|
|
256
|
+
aury migrate merge "abc123,def456" -m "merge branches"
|
|
257
|
+
"""
|
|
258
|
+
try:
|
|
259
|
+
manager = get_manager(config_override=config)
|
|
260
|
+
revision_list = [r.strip() for r in revisions.split(",")]
|
|
261
|
+
|
|
262
|
+
async def _merge():
|
|
263
|
+
result = await manager.merge(revisions=revision_list, message=message)
|
|
264
|
+
typer.echo(f"✅ 迁移已合并: {result}")
|
|
265
|
+
|
|
266
|
+
asyncio.run(_merge())
|
|
267
|
+
except Exception as e:
|
|
268
|
+
_handle_exception(e, "合并迁移")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@app.command()
|
|
272
|
+
def history(
|
|
273
|
+
verbose: bool = typer.Option(False, "-v", "--verbose", help="显示详细信息"),
|
|
274
|
+
config: str | None = typer.Option(None, "--config", help="Alembic 配置文件路径(覆盖默认配置)"),
|
|
275
|
+
) -> None:
|
|
276
|
+
"""显示迁移历史。
|
|
277
|
+
|
|
278
|
+
示例:
|
|
279
|
+
aury migrate history
|
|
280
|
+
aury migrate history --verbose
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
manager = get_manager(config_override=config)
|
|
284
|
+
|
|
285
|
+
async def _history():
|
|
286
|
+
await manager.history(verbose=verbose)
|
|
287
|
+
|
|
288
|
+
asyncio.run(_history())
|
|
289
|
+
except Exception as e:
|
|
290
|
+
_handle_exception(e, "显示历史")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
__all__ = [
|
|
294
|
+
"check",
|
|
295
|
+
"down",
|
|
296
|
+
"history",
|
|
297
|
+
"make",
|
|
298
|
+
"merge",
|
|
299
|
+
"show",
|
|
300
|
+
"status",
|
|
301
|
+
"up",
|
|
302
|
+
]
|
|
303
|
+
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""调度器命令 - 独立运行调度器。
|
|
2
|
+
|
|
3
|
+
使用示例:
|
|
4
|
+
aury scheduler # 运行调度器
|
|
5
|
+
aury scheduler --app main:app # 指定应用模块
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(
|
|
20
|
+
name="scheduler",
|
|
21
|
+
help="🕐 独立运行调度器",
|
|
22
|
+
no_args_is_help=False,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _detect_app_module() -> str:
|
|
27
|
+
"""自动检测应用模块路径。"""
|
|
28
|
+
import os
|
|
29
|
+
|
|
30
|
+
# 1. 环境变量
|
|
31
|
+
if app_module := os.environ.get("APP_MODULE"):
|
|
32
|
+
return app_module
|
|
33
|
+
|
|
34
|
+
# 2. pyproject.toml 中的 [tool.aury] 配置
|
|
35
|
+
pyproject_path = Path.cwd() / "pyproject.toml"
|
|
36
|
+
if pyproject_path.exists():
|
|
37
|
+
try:
|
|
38
|
+
import tomllib
|
|
39
|
+
with open(pyproject_path, "rb") as f:
|
|
40
|
+
data = tomllib.load(f)
|
|
41
|
+
if package := data.get("tool", {}).get("aury", {}).get("package"):
|
|
42
|
+
return f"{package}.main:app"
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# 3. 默认
|
|
47
|
+
return "main:app"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.callback(invoke_without_command=True)
|
|
51
|
+
def run_scheduler(
|
|
52
|
+
ctx: typer.Context,
|
|
53
|
+
app_path: str | None = typer.Option(
|
|
54
|
+
None,
|
|
55
|
+
"--app",
|
|
56
|
+
"-a",
|
|
57
|
+
help="应用模块路径(默认自动检测,如 main:app)",
|
|
58
|
+
),
|
|
59
|
+
) -> None:
|
|
60
|
+
"""独立运行调度器进程。
|
|
61
|
+
|
|
62
|
+
调度器会加载应用中注册的所有定时任务并执行。
|
|
63
|
+
|
|
64
|
+
示例:
|
|
65
|
+
aury scheduler # 自动检测应用
|
|
66
|
+
aury scheduler --app main:app # 指定应用模块
|
|
67
|
+
aury scheduler -a myapp.main:app # 指定包中的应用
|
|
68
|
+
"""
|
|
69
|
+
if ctx.invoked_subcommand is not None:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
# 确保当前目录在 Python 路径中
|
|
73
|
+
cwd = str(Path.cwd())
|
|
74
|
+
if cwd not in sys.path:
|
|
75
|
+
sys.path.insert(0, cwd)
|
|
76
|
+
|
|
77
|
+
app_module = app_path or _detect_app_module()
|
|
78
|
+
console.print("[bold cyan]🕐 启动独立调度器[/bold cyan]")
|
|
79
|
+
console.print(f" 应用: [green]{app_module}[/green]")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
# 导入应用
|
|
83
|
+
module_path, app_name = app_module.rsplit(":", 1)
|
|
84
|
+
module = __import__(module_path, fromlist=[app_name])
|
|
85
|
+
application = getattr(module, app_name)
|
|
86
|
+
|
|
87
|
+
# 设置日志上下文
|
|
88
|
+
from aury.boot.common.logging import set_service_context
|
|
89
|
+
set_service_context("scheduler")
|
|
90
|
+
|
|
91
|
+
# 获取调度器
|
|
92
|
+
from aury.boot.infrastructure.scheduler import SchedulerManager
|
|
93
|
+
scheduler = SchedulerManager.get_instance()
|
|
94
|
+
|
|
95
|
+
console.print("[bold green]✅ 调度器启动成功[/bold green]")
|
|
96
|
+
console.print("[dim]按 Ctrl+C 停止[/dim]")
|
|
97
|
+
|
|
98
|
+
# 运行调度器
|
|
99
|
+
async def _run():
|
|
100
|
+
await scheduler.initialize()
|
|
101
|
+
scheduler.start()
|
|
102
|
+
try:
|
|
103
|
+
# 保持运行
|
|
104
|
+
while True:
|
|
105
|
+
await asyncio.sleep(1)
|
|
106
|
+
except asyncio.CancelledError:
|
|
107
|
+
pass
|
|
108
|
+
finally:
|
|
109
|
+
scheduler.shutdown()
|
|
110
|
+
|
|
111
|
+
asyncio.run(_run())
|
|
112
|
+
|
|
113
|
+
except KeyboardInterrupt:
|
|
114
|
+
console.print("\n[yellow]调度器已停止[/yellow]")
|
|
115
|
+
except ImportError as e:
|
|
116
|
+
console.print(f"[red]❌ 无法导入应用: {e}[/red]")
|
|
117
|
+
console.print("[dim]请确保应用模块路径正确,如 main:app[/dim]")
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
console.print(f"[red]❌ 启动失败: {e}[/red]")
|
|
121
|
+
raise typer.Exit(1)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
__all__ = ["app"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""服务器管理命令。
|
|
2
|
+
|
|
3
|
+
提供 CLI 接口来管理和运行应用服务器。
|
|
4
|
+
|
|
5
|
+
使用示例:
|
|
6
|
+
python -m aury.boot.commands.server run \\
|
|
7
|
+
--host 0.0.0.0 \\
|
|
8
|
+
--port 8000 \\
|
|
9
|
+
--workers 4 \\
|
|
10
|
+
--reload
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .app import app, server_cli
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"app",
|
|
17
|
+
"server_cli",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|