Flowfile 0.3.5__py3-none-any.whl → 0.3.7__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.
Potentially problematic release.
This version of Flowfile might be problematic. Click here for more details.
- flowfile/__init__.py +27 -6
- flowfile/api.py +1 -0
- flowfile/web/__init__.py +2 -2
- flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +86 -0
- flowfile/web/static/assets/CloudConnectionManager-c20a740f.js +783 -0
- flowfile/web/static/assets/CloudStorageReader-29d14fcc.css +143 -0
- flowfile/web/static/assets/CloudStorageReader-960b400a.js +437 -0
- flowfile/web/static/assets/CloudStorageWriter-49c9a4b2.css +138 -0
- flowfile/web/static/assets/CloudStorageWriter-e3decbdd.js +430 -0
- flowfile/web/static/assets/{CrossJoin-dfcf7351.js → CrossJoin-d67e2405.js} +8 -8
- flowfile/web/static/assets/{DatabaseConnectionSettings-b2afb1d7.js → DatabaseConnectionSettings-a81e0f7e.js} +2 -2
- flowfile/web/static/assets/{DatabaseManager-824a49b2.js → DatabaseManager-9ea35e84.js} +2 -2
- flowfile/web/static/assets/{DatabaseReader-a48124d8.js → DatabaseReader-9578bfa5.js} +9 -9
- flowfile/web/static/assets/{DatabaseWriter-b47cbae2.js → DatabaseWriter-19531098.js} +9 -9
- flowfile/web/static/assets/{ExploreData-fdfc45a4.js → ExploreData-40476474.js} +47141 -43697
- flowfile/web/static/assets/{ExternalSource-861b0e71.js → ExternalSource-2297ef96.js} +6 -6
- flowfile/web/static/assets/{Filter-f87bb897.js → Filter-f211c03a.js} +8 -8
- flowfile/web/static/assets/{Formula-b8cefc31.css → Formula-29f19d21.css} +10 -0
- flowfile/web/static/assets/{Formula-1e2ed720.js → Formula-4207ea31.js} +75 -9
- flowfile/web/static/assets/{FuzzyMatch-b6cc4fdd.js → FuzzyMatch-bf120df0.js} +9 -9
- flowfile/web/static/assets/{GraphSolver-6a371f4c.js → GraphSolver-5bb7497a.js} +5 -5
- flowfile/web/static/assets/{GroupBy-f7b7f472.js → GroupBy-92c81b65.js} +6 -6
- flowfile/web/static/assets/{Join-eec38203.js → Join-4e49a274.js} +23 -15
- flowfile/web/static/assets/{Join-41c0f331.css → Join-f45eff22.css} +20 -20
- flowfile/web/static/assets/{ManualInput-9aaa46fb.js → ManualInput-90998ae8.js} +106 -34
- flowfile/web/static/assets/{ManualInput-ac7b9972.css → ManualInput-a71b52c6.css} +29 -17
- flowfile/web/static/assets/{Output-3b2ca045.js → Output-81e3e917.js} +4 -4
- flowfile/web/static/assets/{Pivot-a4f5d88f.js → Pivot-a3419842.js} +6 -6
- flowfile/web/static/assets/{PolarsCode-49ce444f.js → PolarsCode-72710deb.js} +6 -6
- flowfile/web/static/assets/{Read-07acdc9a.js → Read-c4059daf.js} +6 -6
- flowfile/web/static/assets/{RecordCount-6a21da56.js → RecordCount-c2b5e095.js} +5 -5
- flowfile/web/static/assets/{RecordId-949bdc17.js → RecordId-10baf191.js} +6 -6
- flowfile/web/static/assets/{Sample-7afca6e1.js → Sample-3ed9a0ae.js} +5 -5
- flowfile/web/static/assets/{SecretManager-b41c029d.js → SecretManager-0d49c0e8.js} +2 -2
- flowfile/web/static/assets/{Select-32b28406.js → Select-8a02a0b3.js} +8 -8
- flowfile/web/static/assets/{SettingsSection-a0f15a05.js → SettingsSection-4c0f45f5.js} +1 -1
- flowfile/web/static/assets/{Sort-fc6ba0e2.js → Sort-f55c9f9d.js} +6 -6
- flowfile/web/static/assets/{TextToRows-23127596.js → TextToRows-5dbc2145.js} +8 -8
- flowfile/web/static/assets/{UnavailableFields-c42880a3.js → UnavailableFields-a1768e52.js} +2 -2
- flowfile/web/static/assets/{Union-39eecc6c.js → Union-f2aefdc9.js} +5 -5
- flowfile/web/static/assets/{Unique-a0e8fe61.js → Unique-46b250da.js} +8 -8
- flowfile/web/static/assets/{Unpivot-1e2d43f0.js → Unpivot-25ac84cc.js} +5 -5
- flowfile/web/static/assets/api-6ef0dcef.js +80 -0
- flowfile/web/static/assets/{api-44ca9e9c.js → api-a0abbdc7.js} +1 -1
- flowfile/web/static/assets/cloud_storage_reader-aa1415d6.png +0 -0
- flowfile/web/static/assets/{designer-267d44f1.js → designer-13eabd83.js} +36 -34
- flowfile/web/static/assets/{documentation-6c0810a2.js → documentation-b87e7f6f.js} +1 -1
- flowfile/web/static/assets/{dropDown-52790b15.js → dropDown-13564764.js} +1 -1
- flowfile/web/static/assets/{fullEditor-e272b506.js → fullEditor-fd2cd6f9.js} +2 -2
- flowfile/web/static/assets/{genericNodeSettings-4bdcf98e.js → genericNodeSettings-71e11604.js} +3 -3
- flowfile/web/static/assets/{index-e235a8bc.js → index-f6c15e76.js} +59 -22
- flowfile/web/static/assets/{nodeTitle-fc3fc4b7.js → nodeTitle-988d9efe.js} +3 -3
- flowfile/web/static/assets/{secretApi-cdc2a3fd.js → secretApi-dd636aa2.js} +1 -1
- flowfile/web/static/assets/{selectDynamic-96aa82cd.js → selectDynamic-af36165e.js} +3 -3
- flowfile/web/static/assets/{vue-codemirror.esm-25e75a08.js → vue-codemirror.esm-2847001e.js} +2 -1
- flowfile/web/static/assets/{vue-content-loader.es-6c4b1c24.js → vue-content-loader.es-0371da73.js} +1 -1
- flowfile/web/static/index.html +1 -1
- {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/METADATA +9 -4
- {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/RECORD +131 -124
- {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/entry_points.txt +2 -0
- flowfile_core/__init__.py +3 -0
- flowfile_core/auth/jwt.py +39 -0
- flowfile_core/configs/node_store/nodes.py +9 -6
- flowfile_core/configs/settings.py +6 -5
- flowfile_core/database/connection.py +63 -15
- flowfile_core/database/init_db.py +0 -1
- flowfile_core/database/models.py +49 -2
- flowfile_core/flowfile/code_generator/code_generator.py +472 -17
- flowfile_core/flowfile/connection_manager/models.py +1 -1
- flowfile_core/flowfile/database_connection_manager/db_connections.py +216 -2
- flowfile_core/flowfile/extensions.py +1 -1
- flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +259 -0
- flowfile_core/flowfile/flow_data_engine/create/funcs.py +19 -8
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +1062 -311
- flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +12 -2
- flowfile_core/flowfile/flow_data_engine/fuzzy_matching/settings_validator.py +1 -1
- flowfile_core/flowfile/flow_data_engine/join/__init__.py +2 -1
- flowfile_core/flowfile/flow_data_engine/join/utils.py +25 -0
- flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +3 -1
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +29 -22
- flowfile_core/flowfile/flow_data_engine/utils.py +1 -40
- flowfile_core/flowfile/flow_graph.py +718 -253
- flowfile_core/flowfile/flow_graph_utils.py +2 -2
- flowfile_core/flowfile/flow_node/flow_node.py +563 -117
- flowfile_core/flowfile/flow_node/models.py +154 -20
- flowfile_core/flowfile/flow_node/schema_callback.py +3 -2
- flowfile_core/flowfile/handler.py +2 -33
- flowfile_core/flowfile/manage/open_flowfile.py +1 -2
- flowfile_core/flowfile/sources/external_sources/__init__.py +0 -2
- flowfile_core/flowfile/sources/external_sources/factory.py +4 -7
- flowfile_core/flowfile/util/calculate_layout.py +0 -2
- flowfile_core/flowfile/utils.py +35 -26
- flowfile_core/main.py +35 -15
- flowfile_core/routes/cloud_connections.py +77 -0
- flowfile_core/routes/logs.py +2 -7
- flowfile_core/routes/public.py +1 -0
- flowfile_core/routes/routes.py +130 -90
- flowfile_core/routes/secrets.py +72 -14
- flowfile_core/schemas/__init__.py +8 -0
- flowfile_core/schemas/cloud_storage_schemas.py +215 -0
- flowfile_core/schemas/input_schema.py +121 -71
- flowfile_core/schemas/output_model.py +19 -3
- flowfile_core/schemas/schemas.py +150 -12
- flowfile_core/schemas/transform_schema.py +175 -35
- flowfile_core/utils/utils.py +40 -1
- flowfile_core/utils/validate_setup.py +41 -0
- flowfile_frame/__init__.py +9 -1
- flowfile_frame/cloud_storage/frame_helpers.py +39 -0
- flowfile_frame/cloud_storage/secret_manager.py +73 -0
- flowfile_frame/expr.py +28 -1
- flowfile_frame/expr.pyi +76 -61
- flowfile_frame/flow_frame.py +481 -208
- flowfile_frame/flow_frame.pyi +140 -91
- flowfile_frame/flow_frame_methods.py +160 -22
- flowfile_frame/group_frame.py +3 -0
- flowfile_frame/utils.py +25 -3
- flowfile_worker/external_sources/s3_source/main.py +216 -0
- flowfile_worker/external_sources/s3_source/models.py +142 -0
- flowfile_worker/funcs.py +51 -6
- flowfile_worker/models.py +22 -2
- flowfile_worker/routes.py +40 -38
- flowfile_worker/utils.py +1 -1
- test_utils/s3/commands.py +46 -0
- test_utils/s3/data_generator.py +292 -0
- test_utils/s3/demo_data_generator.py +186 -0
- test_utils/s3/fixtures.py +214 -0
- flowfile/web/static/assets/AirbyteReader-1ac35765.css +0 -314
- flowfile/web/static/assets/AirbyteReader-e08044e5.js +0 -922
- flowfile/web/static/assets/dropDownGeneric-60f56a8a.js +0 -72
- flowfile/web/static/assets/dropDownGeneric-895680d6.css +0 -10
- flowfile_core/flowfile/sources/external_sources/airbyte_sources/airbyte.py +0 -159
- flowfile_core/flowfile/sources/external_sources/airbyte_sources/models.py +0 -172
- flowfile_core/flowfile/sources/external_sources/airbyte_sources/settings.py +0 -173
- flowfile_core/schemas/defaults.py +0 -9
- flowfile_core/schemas/external_sources/airbyte_schemas.py +0 -20
- flowfile_core/schemas/models.py +0 -193
- flowfile_worker/external_sources/airbyte_sources/cache_manager.py +0 -161
- flowfile_worker/external_sources/airbyte_sources/main.py +0 -89
- flowfile_worker/external_sources/airbyte_sources/models.py +0 -133
- flowfile_worker/external_sources/airbyte_sources/settings.py +0 -0
- {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/LICENSE +0 -0
- {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/WHEEL +0 -0
- {flowfile_core/flowfile/sources/external_sources/airbyte_sources → flowfile_frame/cloud_storage}/__init__.py +0 -0
- {flowfile_core/schemas/external_sources → flowfile_worker/external_sources/s3_source}/__init__.py +0 -0
- {flowfile_worker/external_sources/airbyte_sources → test_utils/s3}/__init__.py +0 -0
|
@@ -15,10 +15,10 @@ from flowfile_core.configs.utils import MutableBool
|
|
|
15
15
|
DEFAULT_SERVER_HOST = "0.0.0.0"
|
|
16
16
|
DEFAULT_SERVER_PORT = 63578
|
|
17
17
|
DEFAULT_WORKER_PORT = 63579
|
|
18
|
-
SINGLE_FILE_MODE: bool = os.environ.get("
|
|
18
|
+
SINGLE_FILE_MODE: bool = os.environ.get("FLOWFILE_SINGLE_FILE_MODE", "0") == "1"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
OFFLOAD_TO_WORKER = MutableBool(
|
|
21
|
+
OFFLOAD_TO_WORKER = MutableBool(not SINGLE_FILE_MODE)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def parse_args():
|
|
@@ -61,7 +61,7 @@ def get_default_worker_url(worker_port=None):
|
|
|
61
61
|
worker_host = os.getenv("WORKER_HOST", None)
|
|
62
62
|
|
|
63
63
|
if worker_port is None:
|
|
64
|
-
worker_port = os.getenv("
|
|
64
|
+
worker_port = os.getenv("FLOWFILE_WORKER_PORT", DEFAULT_WORKER_PORT)
|
|
65
65
|
|
|
66
66
|
# Convert to int if it's a string
|
|
67
67
|
worker_port = int(worker_port) if isinstance(worker_port, str) else worker_port
|
|
@@ -81,14 +81,15 @@ args = parse_args()
|
|
|
81
81
|
|
|
82
82
|
SERVER_HOST = args.host if args.host is not None else DEFAULT_SERVER_HOST
|
|
83
83
|
SERVER_PORT = args.port if args.port is not None else DEFAULT_SERVER_PORT
|
|
84
|
-
WORKER_PORT = args.worker_port if args.worker_port is not None else int(os.getenv("
|
|
84
|
+
WORKER_PORT = args.worker_port if args.worker_port is not None else int(os.getenv("FLOWFILE_WORKER_PORT",
|
|
85
|
+
DEFAULT_WORKER_PORT))
|
|
85
86
|
WORKER_HOST = os.getenv("WORKER_HOST", "0.0.0.0" if platform.system() != "Windows" else "127.0.0.1")
|
|
86
87
|
|
|
87
88
|
config = Config(".env")
|
|
88
89
|
DEBUG: bool = config("DEBUG", cast=bool, default=False)
|
|
89
90
|
FILE_LOCATION = config("FILE_LOCATION", cast=str, default=".\\files\\")
|
|
90
91
|
AVAILABLE_RAM = config("AVAILABLE_RAM", cast=int, default=8)
|
|
91
|
-
WORKER_URL = config("
|
|
92
|
+
WORKER_URL = config("FLOWFILE_WORKER_URL", cast=str, default=get_default_worker_url(WORKER_PORT))
|
|
92
93
|
IS_RUNNING_IN_DOCKER = os.getenv('RUNNING_IN_DOCKER', 'false').lower() == 'true'
|
|
93
94
|
TEMP_DIR = get_temp_dir()
|
|
94
95
|
|
|
@@ -1,33 +1,69 @@
|
|
|
1
|
-
|
|
2
1
|
from sqlalchemy import create_engine
|
|
3
2
|
from contextlib import contextmanager
|
|
4
3
|
from sqlalchemy.orm import sessionmaker
|
|
5
4
|
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from flowfile_core.configs import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_app_data_dir() -> Path:
|
|
11
|
+
"""Get the appropriate application data directory for the current platform."""
|
|
12
|
+
app_name = "Flowfile"
|
|
13
|
+
|
|
14
|
+
if sys.platform == "win32":
|
|
15
|
+
# Windows: C:\Users\{username}\AppData\Local\flowfile
|
|
16
|
+
base_dir = os.environ.get("LOCALAPPDATA")
|
|
17
|
+
if not base_dir:
|
|
18
|
+
base_dir = os.path.join(os.path.expanduser("~"), "AppData", "Local")
|
|
19
|
+
elif sys.platform == "darwin":
|
|
20
|
+
# macOS: ~/Library/Application Support/flowfile
|
|
21
|
+
base_dir = os.path.join(os.path.expanduser("~"), "Library", "Application Support")
|
|
22
|
+
else:
|
|
23
|
+
# Linux: ~/.local/share/flowfile or use XDG_DATA_HOME
|
|
24
|
+
base_dir = os.environ.get("XDG_DATA_HOME")
|
|
25
|
+
if not base_dir:
|
|
26
|
+
base_dir = os.path.join(os.path.expanduser("~"), ".local", "share")
|
|
27
|
+
|
|
28
|
+
app_dir = Path(base_dir) / app_name
|
|
29
|
+
|
|
30
|
+
print(f"Using application data directory: {app_dir}")
|
|
31
|
+
app_dir.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
return app_dir
|
|
6
34
|
|
|
7
35
|
|
|
8
36
|
def get_database_url():
|
|
37
|
+
"""Get the database URL based on the current environment."""
|
|
9
38
|
if os.environ.get("TESTING") == "True":
|
|
10
|
-
# Use a
|
|
39
|
+
# Use a temporary test database
|
|
11
40
|
test_db_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../test_flowfile.db")
|
|
12
41
|
return f"sqlite:///{test_db_path}"
|
|
13
|
-
# elif os.environ.get("FLOWFILE_MODE") == "electron":
|
|
14
42
|
|
|
15
|
-
|
|
43
|
+
custom_db_path = os.environ.get("FLOWFILE_DB_PATH")
|
|
44
|
+
if custom_db_path:
|
|
45
|
+
# logger.error("Using database URL:", os.environ.get("FLOWFILE_DB_URL"))
|
|
46
|
+
return f"sqlite:///{custom_db_path}"
|
|
47
|
+
# Use centralized location
|
|
48
|
+
app_dir = get_app_data_dir()
|
|
49
|
+
|
|
50
|
+
db_path = app_dir / "flowfile.db"
|
|
51
|
+
logger.info(f"Using database URL: sqlite:///{db_path}")
|
|
52
|
+
return f"sqlite:///{db_path}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_database_path() -> Path:
|
|
56
|
+
"""Get the actual path to the database file (useful for backup/info purposes)."""
|
|
57
|
+
url = get_database_url()
|
|
58
|
+
if url.startswith("sqlite:///"):
|
|
59
|
+
return Path(url.replace("sqlite:///", ""))
|
|
60
|
+
return None
|
|
16
61
|
|
|
17
|
-
# else:
|
|
18
|
-
# # Use PostgreSQL for Docker mode
|
|
19
|
-
# host = os.environ.get("DB_HOST", "localhost")
|
|
20
|
-
# port = os.environ.get("DB_PORT", "5432")
|
|
21
|
-
# user = os.environ.get("DB_USER", "postgres")
|
|
22
|
-
# password = os.environ.get("DB_PASSWORD", "postgres")
|
|
23
|
-
# db = os.environ.get("DB_NAME", "flowfile")
|
|
24
|
-
# return f"postgresql://{user}:{password}@{host}:{port}/{db}"
|
|
25
|
-
#
|
|
26
62
|
|
|
27
63
|
# Create database engine
|
|
28
64
|
engine = create_engine(
|
|
29
65
|
get_database_url(),
|
|
30
|
-
connect_args={"check_same_thread": False} if
|
|
66
|
+
connect_args={"check_same_thread": False} if "sqlite" in get_database_url() else {}
|
|
31
67
|
)
|
|
32
68
|
|
|
33
69
|
# Create session factory
|
|
@@ -35,6 +71,7 @@ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
35
71
|
|
|
36
72
|
|
|
37
73
|
def get_db():
|
|
74
|
+
"""Dependency for FastAPI to get database session."""
|
|
38
75
|
db = SessionLocal()
|
|
39
76
|
try:
|
|
40
77
|
yield db
|
|
@@ -44,8 +81,19 @@ def get_db():
|
|
|
44
81
|
|
|
45
82
|
@contextmanager
|
|
46
83
|
def get_db_context():
|
|
84
|
+
"""Context manager for getting database session."""
|
|
47
85
|
db = SessionLocal()
|
|
48
86
|
try:
|
|
49
87
|
yield db
|
|
50
88
|
finally:
|
|
51
|
-
db.close()
|
|
89
|
+
db.close()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_database_info():
|
|
93
|
+
"""Get information about the current database configuration."""
|
|
94
|
+
return {
|
|
95
|
+
"url": get_database_url(),
|
|
96
|
+
"path": str(get_database_path()) if get_database_path() else None,
|
|
97
|
+
"app_data_dir": str(get_app_data_dir()),
|
|
98
|
+
"platform": sys.platform
|
|
99
|
+
}
|
|
@@ -13,7 +13,6 @@ db_models.Base.metadata.create_all(bind=engine)
|
|
|
13
13
|
|
|
14
14
|
def create_default_local_user(db: Session):
|
|
15
15
|
local_user = db.query(db_models.User).filter(db_models.User.username == "local_user").first()
|
|
16
|
-
|
|
17
16
|
if not local_user:
|
|
18
17
|
random_password = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(32))
|
|
19
18
|
hashed_password = pwd_context.hash(random_password)
|
flowfile_core/database/models.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text
|
|
1
|
+
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text, DateTime
|
|
3
2
|
from sqlalchemy.ext.declarative import declarative_base
|
|
3
|
+
from sqlalchemy.sql import func
|
|
4
4
|
|
|
5
5
|
Base = declarative_base()
|
|
6
6
|
|
|
@@ -39,3 +39,50 @@ class DatabaseConnection(Base):
|
|
|
39
39
|
ssl_enabled = Column(Boolean, default=False)
|
|
40
40
|
password_id = Column(Integer, ForeignKey("secrets.id"))
|
|
41
41
|
user_id = Column(Integer, ForeignKey("users.id"))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CloudStorageConnection(Base):
|
|
45
|
+
__tablename__ = "cloud_storage_connections"
|
|
46
|
+
|
|
47
|
+
id = Column(Integer, primary_key=True, index=True)
|
|
48
|
+
connection_name = Column(String, index=True, nullable=False)
|
|
49
|
+
storage_type = Column(String, nullable=False) # 's3', 'adls', 'gcs'
|
|
50
|
+
auth_method = Column(String, nullable=False) # 'access_key', 'iam_role', etc.
|
|
51
|
+
|
|
52
|
+
# AWS S3 fields
|
|
53
|
+
aws_region = Column(String, nullable=True)
|
|
54
|
+
aws_access_key_id = Column(String, nullable=True)
|
|
55
|
+
aws_secret_access_key_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
|
|
56
|
+
aws_session_token_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
|
|
57
|
+
aws_role_arn = Column(String, nullable=True)
|
|
58
|
+
aws_allow_unsafe_html = Column(Boolean, nullable=True)
|
|
59
|
+
|
|
60
|
+
# Azure ADLS fields
|
|
61
|
+
azure_account_name = Column(String, nullable=True)
|
|
62
|
+
azure_account_key_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
|
|
63
|
+
azure_tenant_id = Column(String, nullable=True)
|
|
64
|
+
azure_client_id = Column(String, nullable=True)
|
|
65
|
+
azure_client_secret_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
|
|
66
|
+
azure_sas_token_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
|
|
67
|
+
|
|
68
|
+
# Common fields
|
|
69
|
+
endpoint_url = Column(String, nullable=True)
|
|
70
|
+
extra_config = Column(Text, nullable=True) # JSON field for additional config
|
|
71
|
+
verify_ssl = Column(Boolean, default=True)
|
|
72
|
+
|
|
73
|
+
# Metadata
|
|
74
|
+
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
75
|
+
created_at = Column(DateTime, default=func.now(), nullable=False)
|
|
76
|
+
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class CloudStoragePermission(Base):
|
|
80
|
+
__tablename__ = "cloud_storage_permissions"
|
|
81
|
+
|
|
82
|
+
id = Column(Integer, primary_key=True, index=True)
|
|
83
|
+
connection_id = Column(Integer, ForeignKey("cloud_storage_connections.id"), nullable=False)
|
|
84
|
+
resource_path = Column(String, nullable=False) # e.g., "s3://bucket-name"
|
|
85
|
+
can_read = Column(Boolean, default=True)
|
|
86
|
+
can_write = Column(Boolean, default=False)
|
|
87
|
+
can_delete = Column(Boolean, default=False)
|
|
88
|
+
can_list = Column(Boolean, default=True)
|