From 1c99d58d648968e62e43a07bdee1590fb3641ab9 Mon Sep 17 00:00:00 2001 From: jingrow Date: Thu, 23 Oct 2025 00:27:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Local=20App?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcloud/api/local_app.py | 238 ++++++++++++++++++ .../jcloud/pagetype/local_app/local_app.json | 134 +++++----- jcloud/jcloud/pagetype/local_app/local_app.py | 13 +- 3 files changed, 308 insertions(+), 77 deletions(-) create mode 100644 jcloud/api/local_app.py diff --git a/jcloud/api/local_app.py b/jcloud/api/local_app.py new file mode 100644 index 0000000..7c3d2cf --- /dev/null +++ b/jcloud/api/local_app.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024, JINGROW +# For license information, please see license.txt + +import jingrow +from jcloud.api.client import dashboard_whitelist +from jcloud.utils import get_current_team + + +@jingrow.whitelist(allow_guest=True) +def get_local_apps(filters=None, order_by=None, limit_start=None, limit_page_length=None): + """获取本地应用列表""" + + # 构建查询条件 + query_filters = {"enabled": 1} # 只获取启用的应用 + + if filters: + if isinstance(filters, str): + import json + filters = json.loads(filters) + query_filters.update(filters) + + # 默认排序 + if not order_by: + order_by = "app_name asc" + + # 获取应用列表 + apps = jingrow.get_all( + "Local App", + filters=query_filters, + fields=[ + "name", "app_name", "title", "subtitle", "category", + "enabled", "public", "team", "status", "repository_url", + "file_url", "app_image", "creation", "modified" + ], + order_by=order_by, + limit_start=limit_start, + limit_page_length=limit_page_length + ) + + # 格式化返回数据 + result = [] + for app in apps: + app_data = { + "name": app.name, + "app_name": app.app_name, + "title": app.title, + "subtitle": app.subtitle, + "category": app.category, + "enabled": app.enabled, + "public": app.public, + "team": app.team, + "status": app.status, + "repository_url": app.repository_url, + "file_url": app.file_url, + "app_image": app.app_image, + "creation": app.creation, + "modified": app.modified + } + result.append(app_data) + + return result + + +@jingrow.whitelist(allow_guest=True) +def get_local_app(name): + """获取本地应用详情""" + + if not jingrow.db.exists("Local App", name): + jingrow.throw("Local App not found") + + app = jingrow.get_pg("Local App", name) + + # 获取应用详情 + app_data = { + "name": app.name, + "app_name": app.app_name, + "title": app.title, + "subtitle": app.subtitle, + "description": app.description, + "category": app.category, + "enabled": app.enabled, + "public": app.public, + "team": app.team, + "status": app.status, + "repository_url": app.repository_url, + "file_url": app.file_url, + "app_image": app.app_image, + "creation": app.creation, + "modified": app.modified + } + + return app_data + + +@dashboard_whitelist() +def create_local_app(app_data): + """创建本地应用""" + team = get_current_team() + + if isinstance(app_data, str): + import json + app_data = json.loads(app_data) + + # 检查应用是否已存在 + if jingrow.db.exists("Local App", app_data["name"]): + jingrow.throw("Local App already exists") + + # 创建应用 + app = jingrow.get_pg({ + "pagetype": "Local App", + "name": app_data["name"], + "app_name": app_data["app_name"], + "title": app_data["title"], + "subtitle": app_data.get("subtitle", ""), + "description": app_data.get("description", ""), + "category": app_data.get("category"), + "team": team.name, + "enabled": app_data.get("enabled", 1), + "public": app_data.get("public", 1), + "status": app_data.get("status", "Draft"), + "repository_url": app_data.get("repository_url"), + "file_url": app_data.get("file_url"), + "app_image": app_data.get("app_image") + }) + + app.insert() + return app.name + + +@dashboard_whitelist() +def update_local_app(name, app_data): + """更新本地应用""" + + if not jingrow.db.exists("Local App", name): + jingrow.throw("Local App not found") + + app = jingrow.get_pg("Local App", name) + + if isinstance(app_data, str): + import json + app_data = json.loads(app_data) + + # 更新字段 + for field, value in app_data.items(): + if hasattr(app, field): + setattr(app, field, value) + + app.save() + return app.name + + +@dashboard_whitelist() +def delete_local_app(name): + """删除本地应用""" + + if not jingrow.db.exists("Local App", name): + jingrow.throw("Local App not found") + + app = jingrow.get_pg("Local App", name) + app.delete() + + return "Local App deleted successfully" + + +@jingrow.whitelist(allow_guest=True) +def get_local_app_categories(): + """获取本地应用分类列表""" + + categories = jingrow.get_all( + "Marketplace App Category", + fields=["name", "description", "slug"], + order_by="name asc" + ) + + result = [] + for category in categories: + category_data = { + "name": category.name, + "description": category.description, + "slug": category.slug + } + result.append(category_data) + + return result + + +@jingrow.whitelist(allow_guest=True) +def search_local_apps(query, filters=None, limit=20): + """搜索本地应用""" + + # 构建搜索条件 + search_filters = { + "enabled": 1, + "app_name": ["like", f"%{query}%"] + } + + if filters: + if isinstance(filters, str): + import json + filters = json.loads(filters) + search_filters.update(filters) + + # 搜索应用 + apps = jingrow.get_all( + "Local App", + filters=search_filters, + fields=[ + "name", "app_name", "title", "subtitle", "category", + "enabled", "public", "team", "status", "repository_url", + "file_url", "app_image", "creation", "modified" + ], + order_by="app_name asc", + limit=limit + ) + + # 格式化返回数据 + result = [] + for app in apps: + app_data = { + "name": app.name, + "app_name": app.app_name, + "title": app.title, + "subtitle": app.subtitle, + "category": app.category, + "enabled": app.enabled, + "public": app.public, + "team": app.team, + "status": app.status, + "repository_url": app.repository_url, + "file_url": app.file_url, + "app_image": app.app_image, + "creation": app.creation, + "modified": app.modified + } + result.append(app_data) + + return result diff --git a/jcloud/jcloud/pagetype/local_app/local_app.json b/jcloud/jcloud/pagetype/local_app/local_app.json index 12a405e..af4642d 100644 --- a/jcloud/jcloud/pagetype/local_app/local_app.json +++ b/jcloud/jcloud/pagetype/local_app/local_app.json @@ -5,52 +5,28 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "app_name", "title", + "subtitle", + "public", "enabled", "column_break_5", + "status", "category", - "public", - "section_break_9", - "url", - "branch", - "repo_owner", - "repo", - "column_break_wbyw", + "file_url", + "repository_url", "team", - "installation", - "enable_auto_deploy", - "skip_review" + "app_image", + "section_break_otbv", + "description" ], "fields": [ - { - "fieldname": "branch", - "fieldtype": "Data", - "label": "Branch", - "set_only_once": 1 - }, - { - "fieldname": "url", - "fieldtype": "Data", - "label": "Repository URL", - "set_only_once": 1 - }, - { - "fieldname": "repo_owner", - "fieldtype": "Data", - "hidden": 1, - "label": "Repository Owner" - }, { "fieldname": "team", "fieldtype": "Link", "label": "Team", - "options": "Team" - }, - { - "fieldname": "installation", - "fieldtype": "Data", - "hidden": 1, - "label": "Installation" + "options": "Team", + "read_only": 1 }, { "default": "0", @@ -58,61 +34,78 @@ "fieldtype": "Check", "label": "Public" }, - { - "fieldname": "repo", - "fieldtype": "Data", - "hidden": 1, - "label": "Repository" - }, { "fieldname": "column_break_5", "fieldtype": "Column Break" }, - { - "fieldname": "section_break_9", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "enable_auto_deploy", - "fieldtype": "Check", - "hidden": 1, - "label": "Enable Auto Deploy" - }, - { - "default": "0", - "fieldname": "skip_review", - "fieldtype": "Check", - "hidden": 1, - "label": "Skip Review" - }, { "default": "1", "fieldname": "enabled", "fieldtype": "Check", "label": "Enabled" }, - { - "fieldname": "title", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Title", - "reqd": 1 - }, { "fieldname": "category", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Category", "options": "Marketplace App Category" }, { - "fieldname": "column_break_wbyw", - "fieldtype": "Column Break" + "fieldname": "section_break_otbv", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Jeditor", + "label": "Description" + }, + { + "fieldname": "repository_url", + "fieldtype": "Data", + "label": "Repository URL" + }, + { + "fieldname": "file_url", + "fieldtype": "Data", + "label": "File URL" + }, + { + "fieldname": "app_image", + "fieldtype": "Attach Image", + "label": "App Image" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "app_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "App Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Published\nUnpublished\nDraft" + }, + { + "fieldname": "subtitle", + "fieldtype": "Small Text", + "label": "Subtitle" } ], "links": [], - "modified": "2025-10-22 18:10:13.615747", + "modified": "2025-10-22 22:35:00.480783", "modified_by": "Administrator", "module": "Jcloud", "name": "Local App", @@ -143,6 +136,7 @@ } ], "row_format": "Dynamic", + "show_title_field_in_link": 1, "sort_field": "modified", "sort_order": "DESC", "states": [], diff --git a/jcloud/jcloud/pagetype/local_app/local_app.py b/jcloud/jcloud/pagetype/local_app/local_app.py index 9e37106..3ad1408 100644 --- a/jcloud/jcloud/pagetype/local_app/local_app.py +++ b/jcloud/jcloud/pagetype/local_app/local_app.py @@ -14,17 +14,16 @@ class LocalApp(Page): if TYPE_CHECKING: from jingrow.types import DF - branch: DF.Data | None + app_image: DF.AttachImage | None + app_name: DF.Data category: DF.Link | None - enable_auto_deploy: DF.Check enabled: DF.Check - installation: DF.Data | None + file_url: DF.Data | None public: DF.Check - repo: DF.Data | None - repo_owner: DF.Data | None - skip_review: DF.Check + repository_url: DF.Data | None + status: DF.Literal["Published", "Unpublished", "Draft"] + subtitle: DF.SmallText | None team: DF.Link | None title: DF.Data - url: DF.Data | None # end: auto-generated types pass