Add project scaffolding and documentation

Add .env.example with configuration for database, Redis, security, SMTP, workers, and plugins. Add .gitignore for Python, Node.js, Next.js, Docker volumes, and IDE files. Add MIT License. Update README.md with feature overview, quick start guide, architecture description, plugin system documentation, security details, backup/restore instructions, and developer setup. Add Alembic configuration files and placeholder directories for API, web, worker, and plugin components
This commit is contained in:
2026-06-21 09:31:47 +02:00
parent cfeeccbf53
commit d694c8b8e3
197 changed files with 8583 additions and 56 deletions
View File
+22
View File
@@ -0,0 +1,22 @@
import uuid
from datetime import datetime
from typing import Any
from pydantic import BaseModel
class AuditLogResponse(BaseModel):
id: uuid.UUID
user_id: uuid.UUID | None
action: str
resource_type: str
resource_id: str | None
ip_address: str | None
user_agent: str | None
details: dict[str, Any]
severity: str
timestamp: datetime
created_at: datetime
class Config:
from_attributes = True
+31
View File
@@ -0,0 +1,31 @@
from datetime import datetime
from pydantic import BaseModel, EmailStr
class LoginRequest(BaseModel):
email: EmailStr
password: str
class TokenResponse(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
expires_at: datetime
class RefreshRequest(BaseModel):
refresh_token: str
class SetupRequest(BaseModel):
email: EmailStr
password: str
first_name: str | None = None
last_name: str | None = None
locale: str = "en"
class SetupStatusResponse(BaseModel):
setup_required: bool
+22
View File
@@ -0,0 +1,22 @@
from typing import Any, Generic, TypeVar
from pydantic import BaseModel
T = TypeVar("T")
class PaginatedResponse(BaseModel, Generic[T]):
items: list[T]
total: int
page: int
page_size: int
pages: int
class MessageResponse(BaseModel):
message: str
class ErrorResponse(BaseModel):
detail: str
errors: dict[str, Any] | None = None
+105
View File
@@ -0,0 +1,105 @@
import uuid
from datetime import datetime
from pydantic import BaseModel, Field
class DashboardWidgetBase(BaseModel):
plugin_id: str
widget_type: str
title: str
description: str | None = None
position_x: int = 0
position_y: int = 0
width: int = 2
height: int = 2
min_width: int | None = None
min_height: int | None = None
max_width: int | None = None
max_height: int | None = None
order_index: int = 0
settings: dict = Field(default_factory=dict)
instance_id: uuid.UUID | None = None
is_visible: bool = True
is_static: bool = False
class DashboardWidgetCreate(DashboardWidgetBase):
pass
class DashboardWidgetUpdate(BaseModel):
title: str | None = None
description: str | None = None
position_x: int | None = None
position_y: int | None = None
width: int | None = None
height: int | None = None
min_width: int | None = None
min_height: int | None = None
max_width: int | None = None
max_height: int | None = None
order_index: int | None = None
settings: dict | None = None
instance_id: uuid.UUID | None = None
is_visible: bool | None = None
is_static: bool | None = None
class DashboardWidgetResponse(DashboardWidgetBase):
id: uuid.UUID
dashboard_id: uuid.UUID
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class DashboardBase(BaseModel):
title: str
description: str | None = None
icon: str | None = None
folder: str = "default"
is_favorite: bool = False
is_public: bool = False
refresh_interval_seconds: int | None = 60
order_index: int = 0
tags: list[str] = Field(default_factory=list)
class DashboardCreate(DashboardBase):
layout: dict = Field(default_factory=dict)
layouts_by_breakpoint: dict = Field(default_factory=lambda: {"lg": [], "md": [], "sm": [], "xs": [], "xxs": []})
class DashboardUpdate(BaseModel):
title: str | None = None
description: str | None = None
icon: str | None = None
folder: str | None = None
is_favorite: bool | None = None
is_public: bool | None = None
refresh_interval_seconds: int | None = None
order_index: int | None = None
tags: list[str] | None = None
layout: dict | None = None
layouts_by_breakpoint: dict | None = None
class DashboardResponse(DashboardBase):
id: uuid.UUID
owner_id: uuid.UUID
layout: dict
layouts_by_breakpoint: dict
widgets: list[DashboardWidgetResponse]
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class DashboardLayoutUpdate(BaseModel):
layout: dict
layouts_by_breakpoint: dict
+32
View File
@@ -0,0 +1,32 @@
import uuid
from datetime import datetime
from typing import Any
from pydantic import BaseModel
class NotificationBase(BaseModel):
title: str
message: str | None = None
type: str = "info"
link: str | None = None
metadata: dict[str, Any] = {}
class NotificationCreate(NotificationBase):
pass
class NotificationUpdate(BaseModel):
is_read: bool
class NotificationResponse(NotificationBase):
id: uuid.UUID
user_id: uuid.UUID
is_read: bool
read_at: datetime | None
created_at: datetime
class Config:
from_attributes = True
+117
View File
@@ -0,0 +1,117 @@
import uuid
from datetime import datetime
from pydantic import BaseModel, Field
class PluginManifest(BaseModel):
id: str
name: str
version: str
nexadashPluginApi: str
description: str | None = None
author: str | None = None
category: str = "Generic"
permissions: list[str] = Field(default_factory=list)
settingsSchema: dict = Field(default_factory=dict)
credentialsSchema: dict = Field(default_factory=dict)
widgets: list[dict] = Field(default_factory=list)
class PluginBase(BaseModel):
id: str
name: str
version: str
description: str | None = None
author: str | None = None
category: str = "Generic"
icon: str | None = None
is_active: bool = False
is_builtin: bool = False
is_installed: bool = False
permissions: list[str] = Field(default_factory=list)
settings_schema: dict = Field(default_factory=dict)
credentials_schema: dict = Field(default_factory=dict)
widget_types: list[dict] = Field(default_factory=list)
healthcheck_definition: dict = Field(default_factory=dict)
api_routes: list[str] = Field(default_factory=list)
has_frontend: bool = True
class PluginResponse(PluginBase):
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class PluginCreate(BaseModel):
id: str
name: str
version: str
description: str | None = None
author: str | None = None
category: str = "Generic"
icon: str | None = None
is_builtin: bool = False
permissions: list[str] = Field(default_factory=list)
settings_schema: dict = Field(default_factory=dict)
credentials_schema: dict = Field(default_factory=dict)
widget_types: list[dict] = Field(default_factory=list)
healthcheck_definition: dict = Field(default_factory=dict)
api_routes: list[str] = Field(default_factory=list)
has_frontend: bool = True
manifest: dict = Field(default_factory=dict)
path: str
class PluginUpdate(BaseModel):
is_active: bool | None = None
is_installed: bool | None = None
class PluginInstanceBase(BaseModel):
plugin_id: str
service_connection_id: uuid.UUID | None = None
name: str
settings: dict = Field(default_factory=dict)
is_enabled: bool = True
class PluginInstanceCreate(PluginInstanceBase):
pass
class PluginInstanceUpdate(BaseModel):
name: str | None = None
service_connection_id: uuid.UUID | None = None
settings: dict | None = None
is_enabled: bool | None = None
class PluginInstanceResponse(PluginInstanceBase):
id: uuid.UUID
health_status: str
health_message: str | None = None
last_sync_at: datetime | None
last_error: str | None
error_count: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class PluginLogResponse(BaseModel):
id: uuid.UUID
plugin_id: str
instance_id: uuid.UUID | None
level: str
message: str
context: dict
timestamp: datetime
class Config:
from_attributes = True
+27
View File
@@ -0,0 +1,27 @@
import uuid
from pydantic import BaseModel
class RoleBase(BaseModel):
name: str
description: str | None = None
permissions: list[str]
class RoleCreate(RoleBase):
pass
class RoleUpdate(BaseModel):
name: str | None = None
description: str | None = None
permissions: list[str] | None = None
class RoleResponse(RoleBase):
id: uuid.UUID
is_system: bool
class Config:
from_attributes = True
+31
View File
@@ -0,0 +1,31 @@
import uuid
from datetime import datetime
from typing import Any
from pydantic import BaseModel
class SecretBase(BaseModel):
name: str
secret_type: str
scope: str
metadata: dict[str, Any] = {}
class SecretCreate(SecretBase):
value: str
class SecretUpdate(BaseModel):
name: str | None = None
value: str | None = None
metadata: dict[str, Any] | None = None
class SecretResponse(SecretBase):
id: uuid.UUID
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
@@ -0,0 +1,49 @@
import uuid
from datetime import datetime
from pydantic import BaseModel, Field
class ServiceConnectionBase(BaseModel):
name: str
plugin_id: str
base_url: str
verify_tls: bool = True
timeout_seconds: int = 30
extra_headers: dict = Field(default_factory=dict)
is_enabled: bool = True
class ServiceConnectionCreate(ServiceConnectionBase):
credentials: dict = Field(default_factory=dict)
class ServiceConnectionUpdate(BaseModel):
name: str | None = None
base_url: str | None = None
verify_tls: bool | None = None
timeout_seconds: int | None = None
extra_headers: dict | None = None
is_enabled: bool | None = None
credentials: dict | None = None
class ServiceConnectionResponse(ServiceConnectionBase):
id: uuid.UUID
credentials_id: uuid.UUID | None
health_status: str
health_message: str | None
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class ServiceConnectionTestRequest(BaseModel):
plugin_id: str
base_url: str
verify_tls: bool = True
timeout_seconds: int = 30
credentials: dict = Field(default_factory=dict)
extra_headers: dict = Field(default_factory=dict)
+19
View File
@@ -0,0 +1,19 @@
from typing import Any
from pydantic import BaseModel
class SystemSettingBase(BaseModel):
key: str
value: dict[str, Any]
description: str | None = None
is_sensitive: bool = False
class SystemSettingUpdate(BaseModel):
value: dict[str, Any]
class SystemSettingResponse(SystemSettingBase):
class Config:
from_attributes = True
+79
View File
@@ -0,0 +1,79 @@
import uuid
from datetime import datetime
from typing import Any
from pydantic import BaseModel, EmailStr, Field
class UserBase(BaseModel):
email: EmailStr
first_name: str | None = None
last_name: str | None = None
locale: str = "en"
theme: str = "system"
timezone: str = "UTC"
class UserCreate(UserBase):
password: str = Field(..., min_length=12, max_length=128)
role_id: uuid.UUID | None = None
class UserUpdate(BaseModel):
email: EmailStr | None = None
first_name: str | None = None
last_name: str | None = None
locale: str | None = None
theme: str | None = None
timezone: str | None = None
role_id: uuid.UUID | None = None
is_active: bool | None = None
class UserResponse(UserBase):
id: uuid.UUID
is_active: bool
is_superuser: bool
is_owner: bool
email_verified: bool
last_login_at: datetime | None
avatar_url: str | None
role: "RoleResponse" | None = None
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class UserInviteRequest(BaseModel):
email: EmailStr
role_id: uuid.UUID
class UserInviteAccept(BaseModel):
token: str
password: str = Field(..., min_length=12, max_length=128)
class PasswordChangeRequest(BaseModel):
current_password: str
new_password: str = Field(..., min_length=12, max_length=128)
class PasswordResetRequest(BaseModel):
email: EmailStr
class PasswordResetConfirm(BaseModel):
token: str
new_password: str = Field(..., min_length=12, max_length=128)
class ProfileUpdate(BaseModel):
first_name: str | None = None
last_name: str | None = None
locale: str | None = None
theme: str | None = None
timezone: str | None = None
preferences: dict[str, Any] | None = None