rainycode 1.5.1__tar.gz → 1.5.3__tar.gz
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.
- {rainycode-1.5.1 → rainycode-1.5.3}/PKG-INFO +1 -1
- rainycode-1.5.3/common_base/aiorequests.py +55 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_models/file_model.py +2 -7
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/api/auth_api.py +5 -4
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/api/wechat_api.py +3 -2
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/router.py +2 -1
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/service/auth_svc.py +4 -3
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/service/file_svc.py +3 -13
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/datetime_util.py +2 -2
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/jwt_util.py +3 -1
- {rainycode-1.5.1 → rainycode-1.5.3}/core/base_config.py +5 -1
- rainycode-1.5.3/pyproject.toml +60 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/rainycode.egg-info/PKG-INFO +1 -1
- rainycode-1.5.1/common_base/aiorequests.py +0 -39
- rainycode-1.5.1/pyproject.toml +0 -36
- {rainycode-1.5.1 → rainycode-1.5.3}/common_base/consts.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_base/exception.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_base/logging.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_base/response.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_depends/auth_depend.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_models/__init__.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_models/base_model.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_models/user_model.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_models/wechat_model.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/schemas/auth_schema.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_servers/service/wechat_svc.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/bcrypt_util.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/captcha_util.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/ip_util.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/snowflake_util.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/common_utils/wechat_util.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/core/databases/aiodb.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/core/databases/aioredis.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/core/middleware/http_middleware.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/core/start.py +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/rainycode.egg-info/SOURCES.txt +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/rainycode.egg-info/dependency_links.txt +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/rainycode.egg-info/requires.txt +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/rainycode.egg-info/top_level.txt +0 -0
- {rainycode-1.5.1 → rainycode-1.5.3}/setup.cfg +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import ssl
|
|
3
|
+
|
|
4
|
+
import aiohttp
|
|
5
|
+
import certifi
|
|
6
|
+
|
|
7
|
+
# 模块级别 SSL 上下文,使用 certifi 证书,避免重复创建
|
|
8
|
+
_SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
|
|
9
|
+
|
|
10
|
+
# 连接池:跨请求复用 keep-alive 长连接(而非每次新建 connector)。
|
|
11
|
+
# limit: 全池同时最大连接数(含活跃与 keep-alive 空闲复用)
|
|
12
|
+
# limit_per_host: 单个 host 端点同时最大连接数,限制对单一对端的并发/占用
|
|
13
|
+
# 惰性初始化:TCPConnector 构造需要运行中的事件循环,只能在首次请求(async 上下文)时创建,
|
|
14
|
+
# 不能在模块 import 时同步 create(否则抛 RuntimeError: no running event loop)。
|
|
15
|
+
_CONNECTOR: aiohttp.TCPConnector | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_connector() -> aiohttp.TCPConnector:
|
|
19
|
+
global _CONNECTOR
|
|
20
|
+
if _CONNECTOR is None:
|
|
21
|
+
_CONNECTOR = aiohttp.TCPConnector(ssl=_SSL_CONTEXT, limit=300, limit_per_host=100)
|
|
22
|
+
return _CONNECTOR
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_url(url, params=None, suffix=None):
|
|
26
|
+
if params:
|
|
27
|
+
query = "&".join([f"{k}={params[k]}" for k in sorted(params.keys())])
|
|
28
|
+
if "?" in url:
|
|
29
|
+
url += "&" + query
|
|
30
|
+
else:
|
|
31
|
+
url += "?" + query
|
|
32
|
+
if suffix:
|
|
33
|
+
url += suffix
|
|
34
|
+
return url
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def get(url, params=None, headers=None, suffix=None):
|
|
38
|
+
url = parse_url(url, params, suffix)
|
|
39
|
+
async with (
|
|
40
|
+
aiohttp.ClientSession(connector=_get_connector(), trust_env=True) as session,
|
|
41
|
+
session.get(url, headers=headers) as response,
|
|
42
|
+
):
|
|
43
|
+
text = await response.text()
|
|
44
|
+
return json.loads(text)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def post(url, data=None, headers=None):
|
|
48
|
+
if isinstance(data, dict):
|
|
49
|
+
data = json.dumps(data)
|
|
50
|
+
async with (
|
|
51
|
+
aiohttp.ClientSession(connector=_get_connector(), trust_env=True) as session,
|
|
52
|
+
session.post(url, data=data, headers=headers) as response,
|
|
53
|
+
):
|
|
54
|
+
text = await response.text()
|
|
55
|
+
return json.loads(text)
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
from core.base_config import base_config
|
|
2
1
|
from tortoise import fields
|
|
3
2
|
|
|
4
3
|
from common_models.base_model import CreateModel
|
|
4
|
+
from core.base_config import base_config
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class File(CreateModel):
|
|
8
8
|
"""
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
url 存最小相对路径(如 dishes/xxx.jpg,不含前缀),经 fix_data 序列化钩子
|
|
12
|
-
(库 model2dict/get_data 会自动调用)加上可配置前缀(/uploads 或完整 CDN URL)
|
|
13
|
-
得到前端可直接访问的完整路径。业务表经外键引用 File.id(如 UserDish.image),
|
|
14
|
-
不冗余存 url 字符串。md5 用于识别相同文件(上传去重)。
|
|
9
|
+
文件表
|
|
15
10
|
"""
|
|
16
11
|
id = fields.IntField(primary_key=True, description="主键")
|
|
17
12
|
name = fields.CharField(max_length=255, description="原始文件名")
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
from fastapi import APIRouter, Body, Query, Request, Security
|
|
2
|
+
|
|
1
3
|
from common_base.response import SuccessReturn
|
|
2
4
|
from common_depends.auth_depend import login_require
|
|
3
5
|
from common_models.user_model import User
|
|
4
|
-
from common_servers.schemas.auth_schema import
|
|
5
|
-
from common_servers.service.auth_svc import AuthService
|
|
6
|
-
from fastapi import APIRouter, Body, Query, Request, Security
|
|
6
|
+
from common_servers.schemas.auth_schema import TokenResponse, UserInfo
|
|
7
|
+
from common_servers.service.auth_svc import AuthService, UserLogin, UserRegister, WechatLogin
|
|
7
8
|
|
|
8
9
|
router = APIRouter(prefix="/auth")
|
|
9
10
|
|
|
@@ -70,4 +71,4 @@ async def get_wechat_oauth_url(
|
|
|
70
71
|
- 如果用户未注册,自动重定向到 snsapi_userinfo 授权
|
|
71
72
|
"""
|
|
72
73
|
authorize_url = await AuthService.get_wechat_oauth_url(app_pk, redirect_uri, state)
|
|
73
|
-
return SuccessReturn(data={"authorize_url": authorize_url})
|
|
74
|
+
return SuccessReturn(data={"authorize_url": authorize_url})
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from urllib.parse import urlencode
|
|
3
3
|
|
|
4
|
+
from fastapi import APIRouter, Query, Request, Response
|
|
5
|
+
from fastapi.responses import RedirectResponse
|
|
6
|
+
|
|
4
7
|
from common_base.consts import REDIS_PREFIX_OAUTH_STATE
|
|
5
8
|
from common_base.exception import CustomException
|
|
6
9
|
from common_servers.service.auth_svc import AuthService
|
|
7
10
|
from common_servers.service.wechat_svc import WechatService
|
|
8
11
|
from core.databases.aioredis import AioRedis
|
|
9
|
-
from fastapi import APIRouter, Query, Request, Response
|
|
10
|
-
from fastapi.responses import RedirectResponse
|
|
11
12
|
|
|
12
13
|
router = APIRouter(prefix="/wechat")
|
|
13
14
|
|
|
@@ -4,6 +4,10 @@ import uuid
|
|
|
4
4
|
from datetime import datetime, timedelta
|
|
5
5
|
from urllib.parse import urlencode
|
|
6
6
|
|
|
7
|
+
from fastapi.responses import RedirectResponse
|
|
8
|
+
from tortoise.expressions import Q
|
|
9
|
+
from tortoise.transactions import atomic
|
|
10
|
+
|
|
7
11
|
from common_base.consts import (
|
|
8
12
|
REDIS_PREFIX_BLOCKLIST,
|
|
9
13
|
REDIS_PREFIX_LOGIN_LOCK_IP,
|
|
@@ -28,9 +32,6 @@ from common_utils.jwt_util import JwtUtil
|
|
|
28
32
|
from common_utils.wechat_util import WechatUtil
|
|
29
33
|
from core.base_config import base_config
|
|
30
34
|
from core.databases.aioredis import AioRedis
|
|
31
|
-
from fastapi.responses import RedirectResponse
|
|
32
|
-
from tortoise.expressions import Q
|
|
33
|
-
from tortoise.transactions import atomic
|
|
34
35
|
|
|
35
36
|
# 登录限流配置
|
|
36
37
|
LOGIN_FAIL_LIMIT = 5 # 最大失败次数
|
|
@@ -7,10 +7,6 @@ from common_models.file_model import File
|
|
|
7
7
|
from common_models.user_model import User
|
|
8
8
|
from core.base_config import base_config
|
|
9
9
|
|
|
10
|
-
# 默认上传约束
|
|
11
|
-
DEFAULT_MAX_BYTES = 5 * 1024 * 1024 # 5MB
|
|
12
|
-
ALLOWED_IMAGE_EXTS = {'jpg', 'jpeg', 'png', 'webp'}
|
|
13
|
-
|
|
14
10
|
|
|
15
11
|
class FileService:
|
|
16
12
|
"""通用文件上传服务:校验格式/大小、写盘、落 File 注册表、按 md5 去重。"""
|
|
@@ -22,15 +18,9 @@ class FileService:
|
|
|
22
18
|
file,
|
|
23
19
|
*,
|
|
24
20
|
subdir: str,
|
|
25
|
-
allowed_exts: set[str] =
|
|
26
|
-
max_bytes: int =
|
|
21
|
+
allowed_exts: set[str] = base_config.upload_allowed_image_exts,
|
|
22
|
+
max_bytes: int = base_config.upload_max_bytes,
|
|
27
23
|
) -> dict:
|
|
28
|
-
"""
|
|
29
|
-
上传文件到 upload_root/<subdir>/ 并落 File 记录,返回序列化结果(url 经 fix_data 加前缀)。
|
|
30
|
-
|
|
31
|
-
相同内容(md5 命中)复用已有 File 记录与磁盘文件,不重复写盘。
|
|
32
|
-
file 为 FastAPI UploadFile(含 .filename/.content_type/.read())。
|
|
33
|
-
"""
|
|
34
24
|
ext = file.filename.rsplit('.', 1)[-1].lower() if '.' in file.filename else ''
|
|
35
25
|
if ext not in allowed_exts:
|
|
36
26
|
raise CustomException(msg="不支持的文件格式")
|
|
@@ -59,4 +49,4 @@ class FileService:
|
|
|
59
49
|
md5=md5,
|
|
60
50
|
creator_id=user.id,
|
|
61
51
|
)
|
|
62
|
-
return await File.model2dict(row)
|
|
52
|
+
return await File.model2dict(row)
|
|
@@ -7,6 +7,6 @@ from zoneinfo import ZoneInfo
|
|
|
7
7
|
CN_TZ = ZoneInfo('Asia/Shanghai')
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
"""返回当前时刻的 aware Asia/Shanghai datetime
|
|
10
|
+
def chinese_now() -> datetime:
|
|
11
|
+
"""返回当前时刻的 aware Asia/Shanghai datetime"""
|
|
12
12
|
return datetime.now(CN_TZ)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from datetime import datetime, timedelta
|
|
2
2
|
from typing import Any
|
|
3
|
+
from zoneinfo import ZoneInfo
|
|
3
4
|
|
|
4
5
|
import jwt
|
|
6
|
+
|
|
5
7
|
from core.base_config import base_config
|
|
6
8
|
|
|
7
9
|
|
|
@@ -25,7 +27,7 @@ class JwtUtil:
|
|
|
25
27
|
str: JWT Token
|
|
26
28
|
"""
|
|
27
29
|
to_encode = data.copy()
|
|
28
|
-
expire = datetime.now() + expires_delta
|
|
30
|
+
expire = datetime.now(ZoneInfo('Asia/Shanghai')) + expires_delta
|
|
29
31
|
|
|
30
32
|
to_encode.update({"exp": expire})
|
|
31
33
|
encoded_jwt = jwt.encode(to_encode, cls.SECRET_KEY, algorithm=cls.ALGORITHM)
|
|
@@ -14,7 +14,7 @@ class BaseConfig(BaseSettings):
|
|
|
14
14
|
base_url: str = ""
|
|
15
15
|
|
|
16
16
|
# 文件上传:本地存储根目录 + 访问 URL 前缀。
|
|
17
|
-
# upload_url_prefix 可为相对路径(/uploads)或完整
|
|
17
|
+
# upload_url_prefix 可为相对路径(/uploads)或完整 URL(https://cdn.xxx.com/uploads),
|
|
18
18
|
upload_root: str = 'uploads'
|
|
19
19
|
upload_url_prefix: str = '/uploads'
|
|
20
20
|
|
|
@@ -32,6 +32,10 @@ class BaseConfig(BaseSettings):
|
|
|
32
32
|
# Refresh Token 有效期
|
|
33
33
|
refresh_token_expire_days: int = 30
|
|
34
34
|
|
|
35
|
+
# 文件上传约束(common_servers/service/file_svc.py)
|
|
36
|
+
upload_max_bytes: int = 5 * 1024 * 1024 # 5MB
|
|
37
|
+
upload_allowed_image_exts: set[str] = {'jpg', 'jpeg', 'png', 'webp'}
|
|
38
|
+
|
|
35
39
|
model_config = SettingsConfigDict(
|
|
36
40
|
env_file=('envs/dev.env', 'envs/pro.env'),
|
|
37
41
|
env_file_encoding="utf-8",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rainycode"
|
|
7
|
+
version = "1.5.3"
|
|
8
|
+
description = "FastAPI base modules"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
"aerich == 0.9.2",
|
|
13
|
+
"fastapi == 0.133.1",
|
|
14
|
+
"aiohttp == 3.13.3",
|
|
15
|
+
"passlib==1.7.4",
|
|
16
|
+
"redis == 7.2.1",
|
|
17
|
+
"pyjwt == 2.11.0",
|
|
18
|
+
"pillow == 12.1.1",
|
|
19
|
+
"bcrypt == 4.0.1",
|
|
20
|
+
"certifi == 2026.2.25",
|
|
21
|
+
"cryptography == 46.0.5",
|
|
22
|
+
"pycryptodome == 3.23.0",
|
|
23
|
+
"uvicorn[standard] == 0.41.0",
|
|
24
|
+
"tortoise-orm[asyncmy] == 0.25.4",
|
|
25
|
+
"pydantic-settings == 2.13.1",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
release = [
|
|
30
|
+
"build == 1.4.0",
|
|
31
|
+
"twine == 6.1.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["."]
|
|
36
|
+
include = ["core*", "common*"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py312" # 按 Python 3.12 语法判断,UP 规则才不会误降级
|
|
41
|
+
line-length = 120 # 中文注释 + FastAPI 调用链较长,120 比 88 舒服
|
|
42
|
+
|
|
43
|
+
[tool.ruff.lint]
|
|
44
|
+
select = [
|
|
45
|
+
"E", "W", # pycodestyle:基础风格(空格/空行/换行)
|
|
46
|
+
"F", # pyflakes:未用 import / 变量、未定义名
|
|
47
|
+
"I", # isort:import 自动排序分组
|
|
48
|
+
"B", # bugbear:常见 bug 模式(可变默认值、裸 except 等)
|
|
49
|
+
"UP", # pyupgrade:语法现代化(Optional[X] → X | None 等)
|
|
50
|
+
]
|
|
51
|
+
ignore = [
|
|
52
|
+
"B008", # 函数调用作为默认值 —— FastAPI 的 Security/Depends/Query/Body 必须这么写
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint.per-file-ignores]
|
|
56
|
+
"migrations/*" = ["ALL"] # aerich 自动生成的迁移文件,不检查
|
|
57
|
+
"__init__.py" = ["F401"] # 模块聚合导出,允许未使用 import
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint.isort]
|
|
60
|
+
known-first-party = ["servers", "models"] # 本项目模块,排序时与第三方库分组
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import ssl
|
|
3
|
-
|
|
4
|
-
import aiohttp
|
|
5
|
-
import certifi
|
|
6
|
-
|
|
7
|
-
# 模块级别 SSL 上下文,使用 certifi 证书,避免重复创建
|
|
8
|
-
_SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def parse_url(url, params=None, suffix=None):
|
|
12
|
-
if params:
|
|
13
|
-
query = "&".join([f"{k}={params[k]}" for k in sorted(params.keys())])
|
|
14
|
-
if "?" in url:
|
|
15
|
-
url += "&" + query
|
|
16
|
-
else:
|
|
17
|
-
url += "?" + query
|
|
18
|
-
if suffix:
|
|
19
|
-
url += suffix
|
|
20
|
-
return url
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
async def get(url, params=None, headers=None, suffix=None):
|
|
24
|
-
url = parse_url(url, params, suffix)
|
|
25
|
-
connector = aiohttp.TCPConnector(ssl=_SSL_CONTEXT)
|
|
26
|
-
async with aiohttp.ClientSession(connector=connector, trust_env=True) as session:
|
|
27
|
-
async with session.get(url, headers=headers) as response:
|
|
28
|
-
text = await response.text()
|
|
29
|
-
return json.loads(text)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
async def post(url, data=None, headers=None):
|
|
33
|
-
connector = aiohttp.TCPConnector(ssl=_SSL_CONTEXT)
|
|
34
|
-
async with aiohttp.ClientSession(connector=connector, trust_env=True) as session:
|
|
35
|
-
if isinstance(data, dict):
|
|
36
|
-
data = json.dumps(data)
|
|
37
|
-
async with session.post(url, data=data, headers=headers) as response:
|
|
38
|
-
text = await response.text()
|
|
39
|
-
return json.loads(text)
|
rainycode-1.5.1/pyproject.toml
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "rainycode"
|
|
7
|
-
version = "1.5.1"
|
|
8
|
-
description = "FastAPI base modules"
|
|
9
|
-
requires-python = ">=3.10"
|
|
10
|
-
|
|
11
|
-
dependencies = [
|
|
12
|
-
"aerich == 0.9.2",
|
|
13
|
-
"fastapi == 0.133.1",
|
|
14
|
-
"aiohttp == 3.13.3",
|
|
15
|
-
"passlib==1.7.4",
|
|
16
|
-
"redis == 7.2.1",
|
|
17
|
-
"pyjwt == 2.11.0",
|
|
18
|
-
"pillow == 12.1.1",
|
|
19
|
-
"bcrypt == 4.0.1",
|
|
20
|
-
"certifi == 2026.2.25",
|
|
21
|
-
"cryptography == 46.0.5",
|
|
22
|
-
"pycryptodome == 3.23.0",
|
|
23
|
-
"uvicorn[standard] == 0.41.0",
|
|
24
|
-
"tortoise-orm[asyncmy] == 0.25.4",
|
|
25
|
-
"pydantic-settings == 2.13.1",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
[project.optional-dependencies]
|
|
29
|
-
release = [
|
|
30
|
-
"build == 1.4.0",
|
|
31
|
-
"twine == 6.1.0",
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
[tool.setuptools.packages.find]
|
|
35
|
-
where = ["."]
|
|
36
|
-
include = ["core*", "common*"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|