skeleton-framework 0.1.0__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.
- skeleton/__init__.py +0 -0
- skeleton/cli.py +151 -0
- skeleton/client.py +269 -0
- skeleton/infrastructure/__init__.py +1 -0
- skeleton/infrastructure/bootstrap/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/commands/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/commands/client/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/commands/client/init.py +486 -0
- skeleton/infrastructure/bootstrap/src/commands/client/install.py +113 -0
- skeleton/infrastructure/bootstrap/src/commands/dev/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/commands/dev/build.py +80 -0
- skeleton/infrastructure/bootstrap/src/commands/dev/init.py +80 -0
- skeleton/infrastructure/bootstrap/src/commands/dev/install.py +168 -0
- skeleton/infrastructure/bootstrap/src/commands/dev/pack.py +55 -0
- skeleton/infrastructure/bootstrap/src/config.py +54 -0
- skeleton/infrastructure/bootstrap/src/copy.py +56 -0
- skeleton/infrastructure/bootstrap/src/load.py +221 -0
- skeleton/infrastructure/bootstrap/src/templates/app/backend/requirements.txt +1 -0
- skeleton/infrastructure/bootstrap/src/templates/app/backend/routes.json +10 -0
- skeleton/infrastructure/bootstrap/src/templates/app/backend/src/__init__.py +0 -0
- skeleton/infrastructure/bootstrap/src/templates/app/backend/src/main.py +35 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/package.json +21 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/skeleton-config.json +29 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/index.html +16 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/main.tsx +15 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/css/styles.css +28 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/img/skeleton.png +0 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/js/script.js +1 -0
- skeleton/infrastructure/bootstrap/src/templates/app/frontend/tsconfig.json +90 -0
- skeleton/infrastructure/bootstrap/src/templates/config/.gitignore +21 -0
- skeleton/infrastructure/bootstrap/src/templates/config/skeleton-dependencies.json +21 -0
- skeleton/infrastructure/bootstrap/src/templates/config/skeleton-layout.json +11 -0
- skeleton/infrastructure/bootstrap/src/templates/config/skeleton-schemas.json +20 -0
- skeleton/infrastructure/bootstrap/src/templates/config/skeleton-servers.json +16 -0
- skeleton/infrastructure/common/__init__.py +1 -0
- skeleton/infrastructure/common/config.json +8 -0
- skeleton/infrastructure/common/src/__init__.py +0 -0
- skeleton/infrastructure/common/src/config.py +37 -0
- skeleton/infrastructure/common/src/constants.py +4 -0
- skeleton/infrastructure/common/src/context.py +15 -0
- skeleton/infrastructure/common/src/env.py +12 -0
- skeleton/infrastructure/common/src/layout.py +68 -0
- skeleton/infrastructure/common/src/loader.py +8 -0
- skeleton/infrastructure/common/src/repository.py +12 -0
- skeleton/infrastructure/common/src/utils.py +26 -0
- skeleton/infrastructure/gateway/__init__.py +0 -0
- skeleton/infrastructure/gateway/settings/proxy.json +24 -0
- skeleton/infrastructure/gateway/settings/services.json +10 -0
- skeleton/infrastructure/gateway/src/__init__.py +0 -0
- skeleton/infrastructure/gateway/src/app.py +98 -0
- skeleton/infrastructure/gateway/src/client.py +68 -0
- skeleton/infrastructure/gateway/src/config.py +29 -0
- skeleton/infrastructure/gateway/src/context.py +31 -0
- skeleton/infrastructure/gateway/src/headers.py +17 -0
- skeleton/infrastructure/gateway/src/lifespan.py +17 -0
- skeleton/infrastructure/gateway/src/loader.py +113 -0
- skeleton/infrastructure/gateway/src/logging.py +37 -0
- skeleton/infrastructure/gateway/src/main.py +139 -0
- skeleton/infrastructure/gateway/src/parser.py +59 -0
- skeleton/infrastructure/gateway/src/registry.py +30 -0
- skeleton/infrastructure/gateway/src/reverseproxy.py +54 -0
- skeleton/infrastructure/gateway/src/router.py +74 -0
- skeleton/infrastructure/gateway/src/schemas.py +21 -0
- skeleton/infrastructure/gateway/src/service.py +123 -0
- skeleton/infrastructure/gateway/src/utils.py +48 -0
- skeleton/infrastructure/gateway/src/validation.py +89 -0
- skeleton/infrastructure/layout.json +12 -0
- skeleton/infrastructure/runners/__init__.py +0 -0
- skeleton/infrastructure/runners/src/__init__.py +0 -0
- skeleton/infrastructure/runners/src/actions.py +4 -0
- skeleton/infrastructure/runners/src/browser.py +18 -0
- skeleton/infrastructure/runners/src/cmd.py +7 -0
- skeleton/infrastructure/runners/src/command.py +7 -0
- skeleton/infrastructure/runners/src/frontend.py +35 -0
- skeleton/infrastructure/runners/src/gateway.py +104 -0
- skeleton/infrastructure/runners/src/hook.py +53 -0
- skeleton/infrastructure/runners/src/main.py +78 -0
- skeleton/infrastructure/runners/src/packages.py +35 -0
- skeleton/infrastructure/runners/src/parser.py +84 -0
- skeleton/infrastructure/runners/src/process.py +99 -0
- skeleton/infrastructure/runners/src/services.py +16 -0
- skeleton/infrastructure/runners/src/utils.py +6 -0
- skeleton/infrastructure/runners/src/validation.py +28 -0
- skeleton/packages/.skeleton/skeleton-cli-1.0.0.tgz +0 -0
- skeleton/packages/.skeleton/skeleton-webpack-1.0.0.tgz +0 -0
- skeleton/root.py +234 -0
- skeleton_framework-0.1.0.dist-info/METADATA +440 -0
- skeleton_framework-0.1.0.dist-info/RECORD +93 -0
- skeleton_framework-0.1.0.dist-info/WHEEL +5 -0
- skeleton_framework-0.1.0.dist-info/entry_points.txt +2 -0
- skeleton_framework-0.1.0.dist-info/licenses/LICENSE +146 -0
- skeleton_framework-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entry": {
|
|
3
|
+
"main": "main",
|
|
4
|
+
"index": "index"
|
|
5
|
+
},
|
|
6
|
+
"lib": {
|
|
7
|
+
"src": "src",
|
|
8
|
+
"dist": "dist",
|
|
9
|
+
"static": "static"
|
|
10
|
+
},
|
|
11
|
+
"devServer": {
|
|
12
|
+
"open": false,
|
|
13
|
+
"historyApiFallback": true,
|
|
14
|
+
"host": "127.0.0.1",
|
|
15
|
+
"port": 80,
|
|
16
|
+
"hot": true,
|
|
17
|
+
"overlay": true
|
|
18
|
+
},
|
|
19
|
+
"proxy": {
|
|
20
|
+
"on": false,
|
|
21
|
+
"host": "127.0.0.1",
|
|
22
|
+
"port": 8888,
|
|
23
|
+
"changeOrigin": true,
|
|
24
|
+
"secure": false
|
|
25
|
+
},
|
|
26
|
+
"tsconfig": {
|
|
27
|
+
"configFile": "./tsconfig.json"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
|
|
8
|
+
<title>Skeleton</title>
|
|
9
|
+
|
|
10
|
+
<link
|
|
11
|
+
rel="icon"
|
|
12
|
+
type="image/x-icon"
|
|
13
|
+
href="/static/img/skeleton.png"
|
|
14
|
+
>
|
|
15
|
+
</head>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRoot } from "react-dom/client";
|
|
2
|
+
|
|
3
|
+
const body = document.querySelector("body");
|
|
4
|
+
|
|
5
|
+
if (!body) {
|
|
6
|
+
throw new Error("Body element not found");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const root = document.createElement("div");
|
|
10
|
+
|
|
11
|
+
body.appendChild(root);
|
|
12
|
+
|
|
13
|
+
createRoot(root).render(
|
|
14
|
+
<h1>Skeleton + FastAPI + React + WebPack</h1>
|
|
15
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*/
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
disregard ANY/ALL padding, margin
|
|
5
|
+
& border when computing width/height
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
body, html {
|
|
13
|
+
height: 100vh;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#root {
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.demo {
|
|
21
|
+
background-color: red;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.alt {
|
|
25
|
+
background-color: blue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*/
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("@scripts/script.js");
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
|
|
4
|
+
/* use modern automatic JSX transform! */
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
|
|
7
|
+
"target": "ES2022", // emit as-is
|
|
8
|
+
"module": "ES2022", // modern import/export syntax
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Using a bundler, like webpack or vite!
|
|
12
|
+
* Don't treat module imports like node!
|
|
13
|
+
* Consume ESM packages like @skeleton/core
|
|
14
|
+
* Enable extensionless imports!
|
|
15
|
+
*/
|
|
16
|
+
"moduleResolution": "bundler",
|
|
17
|
+
|
|
18
|
+
/** Declare which global API's exit in env! */
|
|
19
|
+
"lib": ["DOM", "ES2022"],
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* save information about project graph from last compilation by detecting
|
|
23
|
+
* smallest set of files to re-emit instead of recompiling the entire project,
|
|
24
|
+
* enabling significantly faster subsequent builds by caching metadata
|
|
25
|
+
* This generates a .tsbuildinfo file (add to .gitignore!)
|
|
26
|
+
*/
|
|
27
|
+
"incremental": true,
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fix module compatibility between
|
|
31
|
+
* CommonJS (require) & ES Modules (import)
|
|
32
|
+
*/
|
|
33
|
+
// "esModuleInterop": true,
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* enable imports like ./config.json
|
|
37
|
+
*/
|
|
38
|
+
"resolveJsonModule": true,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Each file to be compiled independently!
|
|
42
|
+
*/
|
|
43
|
+
"isolatedModules": true,
|
|
44
|
+
|
|
45
|
+
/* webpack handles build! */
|
|
46
|
+
"noEmit": true,
|
|
47
|
+
"noEmitOnError": true,
|
|
48
|
+
"sourceMap": true,
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* skip type checking .d.ts files in node_modules
|
|
52
|
+
* Many packages have imperfect/conflicting/legacy types!
|
|
53
|
+
*/
|
|
54
|
+
"skipLibCheck": true,
|
|
55
|
+
|
|
56
|
+
"strict": true,
|
|
57
|
+
"noImplicitAny": true,
|
|
58
|
+
// "noUnusedLocals": true,
|
|
59
|
+
"noImplicitReturns": true,
|
|
60
|
+
// "noUnusedParameters": true,
|
|
61
|
+
|
|
62
|
+
"forceConsistentCasingInFileNames": true,
|
|
63
|
+
// "disableSourceOfProjectReferenceRedirect": true,
|
|
64
|
+
|
|
65
|
+
"removeComments": true,
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Decorators & typescript metadata!
|
|
69
|
+
*/
|
|
70
|
+
/* @decorator/annotation syntax */
|
|
71
|
+
"experimentalDecorators": true,
|
|
72
|
+
"emitDecoratorMetadata": true,
|
|
73
|
+
// "useDefineForClassFields": true,
|
|
74
|
+
|
|
75
|
+
/* uses tsconfig-paths-webpack-plugin */
|
|
76
|
+
"paths": {
|
|
77
|
+
|
|
78
|
+
// common aliases!
|
|
79
|
+
"@styles/*": ["./src/static/css/*"],
|
|
80
|
+
"@scripts/*": ["./src/static/js/*"],
|
|
81
|
+
"@img/*": ["./src/static/img/*"],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
"include": ["./src/**/*",],
|
|
86
|
+
"exclude": [
|
|
87
|
+
"dist",
|
|
88
|
+
"node_modules"
|
|
89
|
+
],
|
|
90
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Skeleton local package artifacts
|
|
2
|
+
.skeleton/
|
|
3
|
+
|
|
4
|
+
# node, js, ts & webpack
|
|
5
|
+
node_modules/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
coverage/
|
|
9
|
+
*.tsbuildinfo
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
*.pyc
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
|
|
19
|
+
# misc
|
|
20
|
+
.DS_Store
|
|
21
|
+
.vscode
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"frontend": {
|
|
3
|
+
"packages": {
|
|
4
|
+
"dependencies": {
|
|
5
|
+
"react": "^19.3.1",
|
|
6
|
+
"react-dom": "^19.3.1"
|
|
7
|
+
},
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@types/react": "^19.3.0",
|
|
10
|
+
"@types/react-dom": "^19.3.0"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"backend": {
|
|
15
|
+
"requirements": {
|
|
16
|
+
"uvicorn": ">=0.34,<1.0",
|
|
17
|
+
"pydantic": ">=2.10,<3.0",
|
|
18
|
+
"sqlalchemy": ">=2.0,<2.1"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from skeleton.infrastructure.common import *
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from skeleton.infrastructure.common.src.loader import load
|
|
4
|
+
from skeleton.infrastructure.common.src.layout import ProjectLayout
|
|
5
|
+
from skeleton.infrastructure.common.src.repository import ConfigRepository
|
|
6
|
+
|
|
7
|
+
def load_runtime_config(
|
|
8
|
+
path: Path,
|
|
9
|
+
project_root: Path,
|
|
10
|
+
framework_root: Path,
|
|
11
|
+
layout: ProjectLayout,
|
|
12
|
+
) -> ConfigRepository:
|
|
13
|
+
|
|
14
|
+
CONFIG = load(path)["config"]
|
|
15
|
+
|
|
16
|
+
data: dict = {}
|
|
17
|
+
|
|
18
|
+
for name, config in CONFIG.items():
|
|
19
|
+
|
|
20
|
+
root = (
|
|
21
|
+
project_root
|
|
22
|
+
if name in ("servers", "schemas")
|
|
23
|
+
else framework_root
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
data[name] = load(root / config)
|
|
27
|
+
|
|
28
|
+
frontend_config = layout.frontend / "skeleton-config.json"
|
|
29
|
+
data["frontend"] = load(frontend_config)
|
|
30
|
+
|
|
31
|
+
return ConfigRepository(
|
|
32
|
+
servers=data["servers"],
|
|
33
|
+
schemas=data["schemas"],
|
|
34
|
+
frontend=data["frontend"],
|
|
35
|
+
proxy=data["proxy"],
|
|
36
|
+
services=data["services"],
|
|
37
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from skeleton.infrastructure.common.src.layout import ProjectLayout
|
|
5
|
+
from skeleton.infrastructure.common.src.repository import ConfigRepository
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, kw_only=True)
|
|
8
|
+
class RuntimeContext:
|
|
9
|
+
root: Path
|
|
10
|
+
mode: str
|
|
11
|
+
args: list[str]
|
|
12
|
+
env: dict[str, str]
|
|
13
|
+
options: dict[str, str | bool]
|
|
14
|
+
config: ConfigRepository
|
|
15
|
+
layout: ProjectLayout
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True, kw_only=True)
|
|
7
|
+
class ProjectLayout:
|
|
8
|
+
frontend: Path
|
|
9
|
+
backend: Path
|
|
10
|
+
infrastructure: Path
|
|
11
|
+
gateway: Path
|
|
12
|
+
runners: Path
|
|
13
|
+
common: Path
|
|
14
|
+
packages: Path
|
|
15
|
+
|
|
16
|
+
def build_layout(
|
|
17
|
+
path: Path,
|
|
18
|
+
project_root: Path,
|
|
19
|
+
framework_root: Path,
|
|
20
|
+
) -> ProjectLayout:
|
|
21
|
+
|
|
22
|
+
if not path.is_file():
|
|
23
|
+
raise FileNotFoundError(
|
|
24
|
+
f"Project layout configuration not found: {path}"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with path.open("r", encoding="utf-8") as file:
|
|
28
|
+
config = json.load(file)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
frontend = config["frontend"]["directory"]
|
|
32
|
+
backend = config["backend"]["directory"]
|
|
33
|
+
|
|
34
|
+
except (KeyError, TypeError) as error:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Invalid project layout configuration: {path}"
|
|
37
|
+
) from error
|
|
38
|
+
|
|
39
|
+
for key, value in {
|
|
40
|
+
"frontend.directory": frontend,
|
|
41
|
+
"backend.directory": backend,
|
|
42
|
+
}.items():
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
not isinstance(value, str)
|
|
46
|
+
or not value.strip()
|
|
47
|
+
or Path(value).is_absolute()
|
|
48
|
+
or len(Path(value).parts) != 1
|
|
49
|
+
or value in (".", "..")
|
|
50
|
+
):
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Invalid directory name for '{key}': {value!r}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
project_root = project_root.resolve()
|
|
56
|
+
framework_root = framework_root.resolve()
|
|
57
|
+
|
|
58
|
+
infrastructure = framework_root / "skeleton" / "infrastructure"
|
|
59
|
+
|
|
60
|
+
return ProjectLayout(
|
|
61
|
+
frontend=project_root / frontend,
|
|
62
|
+
backend=project_root / backend,
|
|
63
|
+
infrastructure=infrastructure,
|
|
64
|
+
gateway=infrastructure / "gateway",
|
|
65
|
+
runners=infrastructure / "runners",
|
|
66
|
+
common=infrastructure / "common",
|
|
67
|
+
packages=framework_root / "skeleton" / "packages",
|
|
68
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
# come back to this, to ensure only
|
|
4
|
+
# load what's needed in each layer
|
|
5
|
+
# store runtime/boostrap confiugration settings!
|
|
6
|
+
@dataclass(frozen=True, kw_only=True)
|
|
7
|
+
class ConfigRepository:
|
|
8
|
+
servers: dict # only gateway
|
|
9
|
+
schemas: dict # only gateway
|
|
10
|
+
frontend: dict # only gateway
|
|
11
|
+
proxy: dict # only gateway
|
|
12
|
+
services: dict # only gateway
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
def resolve_executable_path(path) -> Path:
|
|
5
|
+
if os.name == "nt":
|
|
6
|
+
return Path(path)/".venv"/"Scripts"/"python.exe"
|
|
7
|
+
else:
|
|
8
|
+
return Path(path)/".venv"/"bin"/"python"
|
|
9
|
+
|
|
10
|
+
def check_file_exists(filename: str, path: Path):
|
|
11
|
+
|
|
12
|
+
if not path.exists():
|
|
13
|
+
raise FileNotFoundError(
|
|
14
|
+
f"{filename}: file does not exist {path}"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if not path.is_file():
|
|
18
|
+
raise IsADirectoryError(
|
|
19
|
+
f"{filename}: path is not a file {path}"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def module_to_path(module: str) -> Path:
|
|
23
|
+
return Path(module.replace(".", "/")).with_suffix(".py")
|
|
24
|
+
|
|
25
|
+
def path_to_module(path: Path) -> str:
|
|
26
|
+
return ".".join(path.with_suffix("").parts)
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"settings": {
|
|
3
|
+
"defaultTimeout": 15.0,
|
|
4
|
+
"followRedirects": false
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"cors": {
|
|
8
|
+
"allowOrigins": [
|
|
9
|
+
"http://localhost:8000",
|
|
10
|
+
"http://127.0.0.1:8000",
|
|
11
|
+
"http://localhost:3000",
|
|
12
|
+
"http://127.0.0.1:3000"
|
|
13
|
+
],
|
|
14
|
+
"allowCredentials": true,
|
|
15
|
+
"allowMethods": ["*"],
|
|
16
|
+
"allowHeaders": ["*"]
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"methods": [
|
|
20
|
+
"GET", "POST", "PUT",
|
|
21
|
+
"PATCH", "DELETE", "HEAD",
|
|
22
|
+
"OPTIONS"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from uuid import uuid4
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI, Request
|
|
4
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
+
from fastapi.responses import JSONResponse
|
|
6
|
+
|
|
7
|
+
from skeleton.infrastructure.common.src.context import RuntimeContext
|
|
8
|
+
|
|
9
|
+
from skeleton.infrastructure.gateway.src.router import ProxyRouter
|
|
10
|
+
from skeleton.infrastructure.gateway.src.client import ProxyClient
|
|
11
|
+
from skeleton.infrastructure.gateway.src.reverseproxy import ReverseProxy
|
|
12
|
+
from skeleton.infrastructure.gateway.src.lifespan import HTTPAsyncClientManager
|
|
13
|
+
|
|
14
|
+
from contextlib import asynccontextmanager
|
|
15
|
+
|
|
16
|
+
from skeleton.infrastructure.gateway.src.utils import build_static_dir
|
|
17
|
+
|
|
18
|
+
def create_app(context: RuntimeContext) -> FastAPI:
|
|
19
|
+
|
|
20
|
+
# print(context.config.frontend["lib"]["static"])
|
|
21
|
+
http = HTTPAsyncClientManager(
|
|
22
|
+
context.config.proxy["settings"]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
@asynccontextmanager
|
|
26
|
+
async def lifespan(app: FastAPI):
|
|
27
|
+
await http.startup()
|
|
28
|
+
yield
|
|
29
|
+
await http.shutdown()
|
|
30
|
+
|
|
31
|
+
reverseproxy = FastAPI(
|
|
32
|
+
# name="Reverse Proxy Dev Gateway",
|
|
33
|
+
title="Reverse Proxy Dev Gateway",
|
|
34
|
+
description="Reverse Proxy Dev Gateway",
|
|
35
|
+
version="1.0.0",
|
|
36
|
+
lifespan=lifespan,
|
|
37
|
+
swagger_ui_parameters={
|
|
38
|
+
"docExpansion": "none",
|
|
39
|
+
"displayRequestDuration": True,
|
|
40
|
+
"defaultModelsExpandDepth": -1
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
CORS = context.config.proxy["cors"]
|
|
45
|
+
# set cors headers dynamically!
|
|
46
|
+
reverseproxy.add_middleware(
|
|
47
|
+
CORSMiddleware,
|
|
48
|
+
allow_origins=CORS["allowOrigins"],
|
|
49
|
+
allow_credentials=CORS["allowCredentials"],
|
|
50
|
+
allow_methods=CORS["allowMethods"],
|
|
51
|
+
allow_headers=CORS["allowHeaders"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
router = ProxyRouter(context=context)
|
|
55
|
+
|
|
56
|
+
client = ProxyClient(http)
|
|
57
|
+
proxy = ReverseProxy(router, client)
|
|
58
|
+
|
|
59
|
+
from fastapi.staticfiles import StaticFiles
|
|
60
|
+
static_dir = build_static_dir(context)
|
|
61
|
+
|
|
62
|
+
print("Mounting static directory:")
|
|
63
|
+
print(" URL: ", f"/{context.config.frontend['lib']['static']}")
|
|
64
|
+
print(" directory:", static_dir)
|
|
65
|
+
|
|
66
|
+
reverseproxy.mount(
|
|
67
|
+
f"/{context.config.frontend["lib"]["static"]}",
|
|
68
|
+
StaticFiles(
|
|
69
|
+
directory=str(static_dir),
|
|
70
|
+
check_dir=True
|
|
71
|
+
),
|
|
72
|
+
name="static"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
devtools_uuid = str(uuid4())
|
|
76
|
+
|
|
77
|
+
@reverseproxy.get("/.well-known/appspecific/com.chrome.devtools.json")
|
|
78
|
+
async def chrome_devtools():
|
|
79
|
+
return JSONResponse(
|
|
80
|
+
{
|
|
81
|
+
"workspace": {
|
|
82
|
+
"root": str(context.root),
|
|
83
|
+
"uuid": devtools_uuid,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
METHODS = context.config.proxy["methods"]
|
|
89
|
+
@reverseproxy.api_route(
|
|
90
|
+
"/{path:path}",
|
|
91
|
+
methods=METHODS,
|
|
92
|
+
summary="http",
|
|
93
|
+
tags=["http"],
|
|
94
|
+
)
|
|
95
|
+
async def http_gateway(request: Request):
|
|
96
|
+
return await proxy.handle_http_request(request=request)
|
|
97
|
+
|
|
98
|
+
return reverseproxy
|