| @@ -24,7 +24,8 @@ | |||
| "description": "Welcome to your personalized Investment Analysis Copilot service, where we delve into the depths of stock analysis to provide you with comprehensive insights. \n", | |||
| "is_listed": true, | |||
| "position": 0, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -40,7 +41,8 @@ | |||
| "description": "Code interpreter, clarifying the syntax and semantics of the code.", | |||
| "is_listed": true, | |||
| "position": 13, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -56,7 +58,8 @@ | |||
| "description": "Hello, I am your creative partner in bringing ideas to vivid life! I can assist you in creating stunning designs by leveraging abilities of DALL\u00b7E 3. ", | |||
| "is_listed": true, | |||
| "position": 4, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -72,7 +75,8 @@ | |||
| "description": "Fully SEO Optimized Article including FAQs", | |||
| "is_listed": true, | |||
| "position": 1, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -88,7 +92,8 @@ | |||
| "description": "Generate Flat Style Image", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -104,7 +109,8 @@ | |||
| "description": "A multilingual translator that provides translation capabilities in multiple languages. Input the text you need to translate and select the target language.", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -120,7 +126,8 @@ | |||
| "description": "I am a YouTube Channel Data Analysis Copilot, I am here to provide expert data analysis tailored to your needs. ", | |||
| "is_listed": true, | |||
| "position": 2, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -136,7 +143,8 @@ | |||
| "description": "Meeting minutes generator", | |||
| "is_listed": true, | |||
| "position": 0, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -152,7 +160,8 @@ | |||
| "description": "Tell me the main elements, I will generate a cyberpunk style image for you. ", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -168,7 +177,8 @@ | |||
| "description": "Write SQL from natural language by pasting in your schema with the request.Please describe your query requirements in natural language and select the target database type.", | |||
| "is_listed": true, | |||
| "position": 13, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -184,7 +194,8 @@ | |||
| "description": "Welcome to your personalized travel service with Consultant! \ud83c\udf0d\u2708\ufe0f Ready to embark on a journey filled with adventure and relaxation? Let's dive into creating your unforgettable travel experience. ", | |||
| "is_listed": true, | |||
| "position": 3, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -200,7 +211,8 @@ | |||
| "description": "I can answer your questions related to strategic marketing.", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -216,7 +228,8 @@ | |||
| "description": "A simulated front-end interviewer that tests the skill level of front-end development through questioning.", | |||
| "is_listed": true, | |||
| "position": 19, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -232,7 +245,8 @@ | |||
| "description": "I'm here to hear about your feature request about Dify and help you flesh it out further. What's on your mind?", | |||
| "is_listed": true, | |||
| "position": 6, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| } | |||
| ] | |||
| }, | |||
| @@ -261,7 +275,8 @@ | |||
| "description": "\u4e00\u4e2a\u6a21\u62df\u7684\u524d\u7aef\u9762\u8bd5\u5b98\uff0c\u901a\u8fc7\u63d0\u95ee\u7684\u65b9\u5f0f\u5bf9\u524d\u7aef\u5f00\u53d1\u7684\u6280\u80fd\u6c34\u5e73\u8fdb\u884c\u68c0\u9a8c\u3002", | |||
| "is_listed": true, | |||
| "position": 20, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -277,7 +292,8 @@ | |||
| "description": "\u8f93\u5165\u76f8\u5173\u5143\u7d20\uff0c\u4e3a\u4f60\u751f\u6210\u6241\u5e73\u63d2\u753b\u98ce\u683c\u7684\u5c01\u9762\u56fe\u7247", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -293,7 +309,8 @@ | |||
| "description": "\u4e00\u4e2a\u591a\u8bed\u8a00\u7ffb\u8bd1\u5668\uff0c\u63d0\u4f9b\u591a\u79cd\u8bed\u8a00\u7ffb\u8bd1\u80fd\u529b\uff0c\u8f93\u5165\u4f60\u9700\u8981\u7ffb\u8bd1\u7684\u6587\u672c\uff0c\u9009\u62e9\u76ee\u6807\u8bed\u8a00\u5373\u53ef\u3002\u63d0\u793a\u8bcd\u6765\u81ea\u5b9d\u7389\u3002", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -309,7 +326,8 @@ | |||
| "description": "\u6211\u5c06\u5e2e\u52a9\u4f60\u628a\u81ea\u7136\u8bed\u8a00\u8f6c\u5316\u6210\u6307\u5b9a\u7684\u6570\u636e\u5e93\u67e5\u8be2 SQL \u8bed\u53e5\uff0c\u8bf7\u5728\u4e0b\u65b9\u8f93\u5165\u4f60\u9700\u8981\u67e5\u8be2\u7684\u6761\u4ef6\uff0c\u5e76\u9009\u62e9\u76ee\u6807\u6570\u636e\u5e93\u7c7b\u578b\u3002", | |||
| "is_listed": true, | |||
| "position": 12, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -325,7 +343,8 @@ | |||
| "description": "\u9610\u660e\u4ee3\u7801\u7684\u8bed\u6cd5\u548c\u8bed\u4e49\u3002", | |||
| "is_listed": true, | |||
| "position": 2, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -341,7 +360,8 @@ | |||
| "description": "\u8f93\u5165\u76f8\u5173\u5143\u7d20\uff0c\u4e3a\u4f60\u751f\u6210\u8d5b\u535a\u670b\u514b\u98ce\u683c\u7684\u63d2\u753b", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -357,7 +377,8 @@ | |||
| "description": "\u6211\u662f\u4e00\u540dSEO\u4e13\u5bb6\uff0c\u53ef\u4ee5\u6839\u636e\u60a8\u63d0\u4f9b\u7684\u6807\u9898\u3001\u5173\u952e\u8bcd\u3001\u76f8\u5173\u4fe1\u606f\u6765\u6279\u91cf\u751f\u6210SEO\u6587\u7ae0\u3002", | |||
| "is_listed": true, | |||
| "position": 10, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -373,7 +394,8 @@ | |||
| "description": "\u5e2e\u4f60\u91cd\u65b0\u7ec4\u7ec7\u548c\u8f93\u51fa\u6df7\u4e71\u590d\u6742\u7684\u4f1a\u8bae\u7eaa\u8981\u3002", | |||
| "is_listed": true, | |||
| "position": 6, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -389,7 +411,8 @@ | |||
| "description": "\u6b22\u8fce\u4f7f\u7528\u60a8\u7684\u4e2a\u6027\u5316\u7f8e\u80a1\u6295\u8d44\u5206\u6790\u52a9\u624b\uff0c\u5728\u8fd9\u91cc\u6211\u4eec\u6df1\u5165\u7684\u8fdb\u884c\u80a1\u7968\u5206\u6790\uff0c\u4e3a\u60a8\u63d0\u4f9b\u5168\u9762\u7684\u6d1e\u5bdf\u3002", | |||
| "is_listed": true, | |||
| "position": 0, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -405,7 +428,8 @@ | |||
| "description": "\u60a8\u597d\uff0c\u6211\u662f\u60a8\u7684\u521b\u610f\u4f19\u4f34\uff0c\u5c06\u5e2e\u52a9\u60a8\u5c06\u60f3\u6cd5\u751f\u52a8\u5730\u5b9e\u73b0\uff01\u6211\u53ef\u4ee5\u534f\u52a9\u60a8\u5229\u7528DALL\u00b7E 3\u7684\u80fd\u529b\u521b\u9020\u51fa\u4ee4\u4eba\u60ca\u53f9\u7684\u8bbe\u8ba1\u3002", | |||
| "is_listed": true, | |||
| "position": 4, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -421,7 +445,8 @@ | |||
| "description": "\u7ffb\u8bd1\u4e13\u5bb6\uff1a\u63d0\u4f9b\u4e2d\u82f1\u6587\u4e92\u8bd1", | |||
| "is_listed": true, | |||
| "position": 4, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -437,7 +462,8 @@ | |||
| "description": "\u60a8\u7684\u79c1\u4eba\u5b66\u4e60\u5bfc\u5e08\uff0c\u5e2e\u60a8\u5236\u5b9a\u5b66\u4e60\u8ba1\u5212\u5e76\u8f85\u5bfc", | |||
| "is_listed": true, | |||
| "position": 26, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -453,7 +479,8 @@ | |||
| "description": "\u5e2e\u4f60\u64b0\u5199\u8bba\u6587\u6587\u732e\u7efc\u8ff0", | |||
| "is_listed": true, | |||
| "position": 7, | |||
| "privacy_policy": "https://dify.ai" | |||
| "privacy_policy": "https://dify.ai", | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -469,7 +496,8 @@ | |||
| "description": "\u4f60\u597d\uff0c\u544a\u8bc9\u6211\u60a8\u60f3\u5206\u6790\u7684 YouTube \u9891\u9053\uff0c\u6211\u5c06\u4e3a\u60a8\u6574\u7406\u4e00\u4efd\u5b8c\u6574\u7684\u6570\u636e\u5206\u6790\u62a5\u544a\u3002", | |||
| "is_listed": true, | |||
| "position": 0, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| }, | |||
| { | |||
| "app": { | |||
| @@ -485,7 +513,8 @@ | |||
| "description": "\u6b22\u8fce\u4f7f\u7528\u60a8\u7684\u4e2a\u6027\u5316\u65c5\u884c\u670d\u52a1\u987e\u95ee\uff01\ud83c\udf0d\u2708\ufe0f \u51c6\u5907\u597d\u8e0f\u4e0a\u4e00\u6bb5\u5145\u6ee1\u5192\u9669\u4e0e\u653e\u677e\u7684\u65c5\u7a0b\u4e86\u5417\uff1f\u8ba9\u6211\u4eec\u4e00\u8d77\u6df1\u5165\u6253\u9020\u60a8\u96be\u5fd8\u7684\u65c5\u884c\u4f53\u9a8c\u5427\u3002", | |||
| "is_listed": true, | |||
| "position": 0, | |||
| "privacy_policy": null | |||
| "privacy_policy": null, | |||
| "custom_disclaimer": null | |||
| } | |||
| ] | |||
| }, | |||
| @@ -48,6 +48,7 @@ class InsertExploreAppListApi(Resource): | |||
| parser.add_argument('desc', type=str, location='json') | |||
| parser.add_argument('copyright', type=str, location='json') | |||
| parser.add_argument('privacy_policy', type=str, location='json') | |||
| parser.add_argument('custom_disclaimer', type=str, location='json') | |||
| parser.add_argument('language', type=supported_language, required=True, nullable=False, location='json') | |||
| parser.add_argument('category', type=str, required=True, nullable=False, location='json') | |||
| parser.add_argument('position', type=int, required=True, nullable=False, location='json') | |||
| @@ -62,6 +63,7 @@ class InsertExploreAppListApi(Resource): | |||
| desc = args['desc'] if args['desc'] else '' | |||
| copy_right = args['copyright'] if args['copyright'] else '' | |||
| privacy_policy = args['privacy_policy'] if args['privacy_policy'] else '' | |||
| custom_disclaimer = args['custom_disclaimer'] if args['custom_disclaimer'] else '' | |||
| else: | |||
| desc = site.description if site.description else \ | |||
| args['desc'] if args['desc'] else '' | |||
| @@ -69,6 +71,8 @@ class InsertExploreAppListApi(Resource): | |||
| args['copyright'] if args['copyright'] else '' | |||
| privacy_policy = site.privacy_policy if site.privacy_policy else \ | |||
| args['privacy_policy'] if args['privacy_policy'] else '' | |||
| custom_disclaimer = site.custom_disclaimer if site.custom_disclaimer else \ | |||
| args['custom_disclaimer'] if args['custom_disclaimer'] else '' | |||
| recommended_app = RecommendedApp.query.filter(RecommendedApp.app_id == args['app_id']).first() | |||
| @@ -78,6 +82,7 @@ class InsertExploreAppListApi(Resource): | |||
| description=desc, | |||
| copyright=copy_right, | |||
| privacy_policy=privacy_policy, | |||
| custom_disclaimer=custom_disclaimer, | |||
| language=args['language'], | |||
| category=args['category'], | |||
| position=args['position'] | |||
| @@ -93,6 +98,7 @@ class InsertExploreAppListApi(Resource): | |||
| recommended_app.description = desc | |||
| recommended_app.copyright = copy_right | |||
| recommended_app.privacy_policy = privacy_policy | |||
| recommended_app.custom_disclaimer = custom_disclaimer | |||
| recommended_app.language = args['language'] | |||
| recommended_app.category = args['category'] | |||
| recommended_app.position = args['position'] | |||
| @@ -23,6 +23,7 @@ def parse_app_site_args(): | |||
| parser.add_argument('customize_domain', type=str, required=False, location='json') | |||
| parser.add_argument('copyright', type=str, required=False, location='json') | |||
| parser.add_argument('privacy_policy', type=str, required=False, location='json') | |||
| parser.add_argument('custom_disclaimer', type=str, required=False, location='json') | |||
| parser.add_argument('customize_token_strategy', type=str, choices=['must', 'allow', 'not_allow'], | |||
| required=False, | |||
| location='json') | |||
| @@ -56,6 +57,7 @@ class AppSite(Resource): | |||
| 'customize_domain', | |||
| 'copyright', | |||
| 'privacy_policy', | |||
| 'custom_disclaimer', | |||
| 'customize_token_strategy', | |||
| 'prompt_public' | |||
| ]: | |||
| @@ -21,6 +21,7 @@ recommended_app_fields = { | |||
| 'description': fields.String(attribute='description'), | |||
| 'copyright': fields.String, | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'category': fields.String, | |||
| 'position': fields.Integer, | |||
| 'is_listed': fields.Boolean | |||
| @@ -116,6 +116,7 @@ class ToolApiProviderAddApi(Resource): | |||
| parser.add_argument('provider', type=str, required=True, nullable=False, location='json') | |||
| parser.add_argument('icon', type=dict, required=True, nullable=False, location='json') | |||
| parser.add_argument('privacy_policy', type=str, required=False, nullable=True, location='json') | |||
| parser.add_argument('custom_disclaimer', type=str, required=False, nullable=True, location='json') | |||
| args = parser.parse_args() | |||
| @@ -128,6 +129,7 @@ class ToolApiProviderAddApi(Resource): | |||
| args['schema_type'], | |||
| args['schema'], | |||
| args.get('privacy_policy', ''), | |||
| args.get('custom_disclaimer', ''), | |||
| ) | |||
| class ToolApiProviderGetRemoteSchemaApi(Resource): | |||
| @@ -186,6 +188,7 @@ class ToolApiProviderUpdateApi(Resource): | |||
| parser.add_argument('original_provider', type=str, required=True, nullable=False, location='json') | |||
| parser.add_argument('icon', type=dict, required=True, nullable=False, location='json') | |||
| parser.add_argument('privacy_policy', type=str, required=True, nullable=True, location='json') | |||
| parser.add_argument('custom_disclaimer', type=str, required=True, nullable=True, location='json') | |||
| args = parser.parse_args() | |||
| @@ -199,6 +202,7 @@ class ToolApiProviderUpdateApi(Resource): | |||
| args['schema_type'], | |||
| args['schema'], | |||
| args['privacy_policy'], | |||
| args['custom_disclaimer'], | |||
| ) | |||
| class ToolApiProviderDeleteApi(Resource): | |||
| @@ -31,6 +31,7 @@ class AppSiteApi(WebApiResource): | |||
| 'description': fields.String, | |||
| 'copyright': fields.String, | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'default_language': fields.String, | |||
| 'prompt_public': fields.Boolean | |||
| } | |||
| @@ -487,7 +487,8 @@ class ToolManager: | |||
| 'icon': icon, | |||
| 'description': provider.description, | |||
| 'credentials': masked_credentials, | |||
| 'privacy_policy': provider.privacy_policy | |||
| 'privacy_policy': provider.privacy_policy, | |||
| 'custom_disclaimer': provider.custom_disclaimer | |||
| }) | |||
| @classmethod | |||
| @@ -113,6 +113,7 @@ site_fields = { | |||
| 'customize_domain': fields.String, | |||
| 'copyright': fields.String, | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'customize_token_strategy': fields.String, | |||
| 'prompt_public': fields.Boolean, | |||
| 'app_base_url': fields.String, | |||
| @@ -146,6 +147,7 @@ app_site_fields = { | |||
| 'customize_domain': fields.String, | |||
| 'copyright': fields.String, | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'customize_token_strategy': fields.String, | |||
| 'prompt_public': fields.Boolean | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| """Custom Disclaimer | |||
| Revision ID: 5fda94355fce | |||
| Revises: 47cc7df8c4f3 | |||
| Create Date: 2024-05-10 20:04:45.806549 | |||
| """ | |||
| import sqlalchemy as sa | |||
| from alembic import op | |||
| import models as models | |||
| # revision identifiers, used by Alembic. | |||
| revision = '5fda94355fce' | |||
| down_revision = '47cc7df8c4f3' | |||
| branch_labels = None | |||
| depends_on = None | |||
| def upgrade(): | |||
| # ### commands auto generated by Alembic - please adjust! ### | |||
| with op.batch_alter_table('recommended_apps', schema=None) as batch_op: | |||
| batch_op.add_column(sa.Column('custom_disclaimer', sa.String(length=255), nullable=False)) | |||
| with op.batch_alter_table('sites', schema=None) as batch_op: | |||
| batch_op.add_column(sa.Column('custom_disclaimer', sa.String(length=255), nullable=True)) | |||
| with op.batch_alter_table('tool_api_providers', schema=None) as batch_op: | |||
| batch_op.add_column(sa.Column('custom_disclaimer', sa.String(length=255), nullable=True)) | |||
| # ### end Alembic commands ### | |||
| def downgrade(): | |||
| # ### commands auto generated by Alembic - please adjust! ### | |||
| with op.batch_alter_table('tool_api_providers', schema=None) as batch_op: | |||
| batch_op.drop_column('custom_disclaimer') | |||
| with op.batch_alter_table('sites', schema=None) as batch_op: | |||
| batch_op.drop_column('custom_disclaimer') | |||
| with op.batch_alter_table('recommended_apps', schema=None) as batch_op: | |||
| batch_op.drop_column('custom_disclaimer') | |||
| # ### end Alembic commands ### | |||
| @@ -435,6 +435,7 @@ class RecommendedApp(db.Model): | |||
| description = db.Column(db.JSON, nullable=False) | |||
| copyright = db.Column(db.String(255), nullable=False) | |||
| privacy_policy = db.Column(db.String(255), nullable=False) | |||
| custom_disclaimer = db.Column(db.String(255), nullable=False) | |||
| category = db.Column(db.String(255), nullable=False) | |||
| position = db.Column(db.Integer, nullable=False, default=0) | |||
| is_listed = db.Column(db.Boolean, nullable=False, default=True) | |||
| @@ -1042,6 +1043,7 @@ class Site(db.Model): | |||
| default_language = db.Column(db.String(255), nullable=False) | |||
| copyright = db.Column(db.String(255)) | |||
| privacy_policy = db.Column(db.String(255)) | |||
| custom_disclaimer = db.Column(db.String(255)) | |||
| customize_domain = db.Column(db.String(255)) | |||
| customize_token_strategy = db.Column(db.String(255), nullable=False) | |||
| prompt_public = db.Column(db.Boolean, nullable=False, server_default=db.text('false')) | |||
| @@ -107,6 +107,8 @@ class ApiToolProvider(db.Model): | |||
| credentials_str = db.Column(db.Text, nullable=False) | |||
| # privacy policy | |||
| privacy_policy = db.Column(db.String(255), nullable=True) | |||
| # custom_disclaimer | |||
| custom_disclaimer = db.Column(db.String(255), nullable=True) | |||
| created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)')) | |||
| updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)')) | |||
| @@ -86,6 +86,7 @@ class RecommendedAppService: | |||
| 'description': site.description, | |||
| 'copyright': site.copyright, | |||
| 'privacy_policy': site.privacy_policy, | |||
| 'custom_disclaimer': site.custom_disclaimer, | |||
| 'category': recommended_app.category, | |||
| 'position': recommended_app.position, | |||
| 'is_listed': recommended_app.is_listed | |||
| @@ -177,7 +177,7 @@ class ToolManageService: | |||
| @staticmethod | |||
| def create_api_tool_provider( | |||
| user_id: str, tenant_id: str, provider_name: str, icon: dict, credentials: dict, | |||
| schema_type: str, schema: str, privacy_policy: str | |||
| schema_type: str, schema: str, privacy_policy: str, custom_disclaimer: str | |||
| ): | |||
| """ | |||
| create api tool provider | |||
| @@ -213,7 +213,8 @@ class ToolManageService: | |||
| schema_type_str=schema_type, | |||
| tools_str=json.dumps(jsonable_encoder(tool_bundles)), | |||
| credentials_str={}, | |||
| privacy_policy=privacy_policy | |||
| privacy_policy=privacy_policy, | |||
| custom_disclaimer=custom_disclaimer | |||
| ) | |||
| if 'auth_type' not in credentials: | |||
| @@ -364,7 +365,7 @@ class ToolManageService: | |||
| @staticmethod | |||
| def update_api_tool_provider( | |||
| user_id: str, tenant_id: str, provider_name: str, original_provider: str, icon: dict, credentials: dict, | |||
| schema_type: str, schema: str, privacy_policy: str | |||
| schema_type: str, schema: str, privacy_policy: str, custom_disclaimer: str | |||
| ): | |||
| """ | |||
| update api tool provider | |||
| @@ -394,6 +395,7 @@ class ToolManageService: | |||
| provider.schema_type_str = ApiProviderSchemaType.OPENAPI.value | |||
| provider.tools_str = json.dumps(jsonable_encoder(tool_bundles)) | |||
| provider.privacy_policy = privacy_policy | |||
| provider.custom_disclaimer = custom_disclaimer | |||
| if 'auth_type' not in credentials: | |||
| raise ValueError('auth_type is required') | |||
| @@ -67,6 +67,7 @@ export type IChatProps = { | |||
| visionConfig?: VisionSettings | |||
| supportAnnotation?: boolean | |||
| allToolIcons?: Record<string, string | Emoji> | |||
| customDisclaimer?: string | |||
| } | |||
| const Chat: FC<IChatProps> = ({ | |||
| @@ -102,6 +103,7 @@ const Chat: FC<IChatProps> = ({ | |||
| supportAnnotation, | |||
| onChatListChange, | |||
| allToolIcons, | |||
| customDisclaimer, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const { notify } = useContext(ToastContext) | |||
| @@ -358,44 +360,46 @@ const Chat: FC<IChatProps> = ({ | |||
| </div> | |||
| </div> | |||
| )} | |||
| <div className={cn('p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}> | |||
| {visionConfig?.enabled && ( | |||
| <> | |||
| <div className='absolute bottom-2 left-2 flex items-center'> | |||
| <ChatImageUploader | |||
| settings={visionConfig} | |||
| onUpload={onUpload} | |||
| disabled={files.length >= visionConfig.number_limits} | |||
| /> | |||
| <div className='mx-1 w-[1px] h-4 bg-black/5' /> | |||
| </div> | |||
| <div className='pl-[52px]'> | |||
| <ImageList | |||
| list={files} | |||
| onRemove={onRemove} | |||
| onReUpload={onReUpload} | |||
| onImageLinkLoadSuccess={onImageLinkLoadSuccess} | |||
| onImageLinkLoadError={onImageLinkLoadError} | |||
| /> | |||
| </div> | |||
| </> | |||
| )} | |||
| <Textarea | |||
| className={` | |||
| block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none | |||
| ${visionConfig?.enabled && 'pl-12'} | |||
| `} | |||
| value={query} | |||
| onChange={handleContentChange} | |||
| onKeyUp={handleKeyUp} | |||
| onKeyDown={handleKeyDown} | |||
| onPaste={onPaste} | |||
| onDragEnter={onDragEnter} | |||
| onDragLeave={onDragLeave} | |||
| onDragOver={onDragOver} | |||
| onDrop={onDrop} | |||
| autoSize | |||
| /> | |||
| <div className='relative'> | |||
| <div className={cn('relative p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}> | |||
| {visionConfig?.enabled && ( | |||
| <> | |||
| <div className='absolute bottom-2 left-2 flex items-center'> | |||
| <ChatImageUploader | |||
| settings={visionConfig} | |||
| onUpload={onUpload} | |||
| disabled={files.length >= visionConfig.number_limits} | |||
| /> | |||
| <div className='mx-1 w-[1px] h-4 bg-black/5' /> | |||
| </div> | |||
| <div className='pl-[52px]'> | |||
| <ImageList | |||
| list={files} | |||
| onRemove={onRemove} | |||
| onReUpload={onReUpload} | |||
| onImageLinkLoadSuccess={onImageLinkLoadSuccess} | |||
| onImageLinkLoadError={onImageLinkLoadError} | |||
| /> | |||
| </div> | |||
| </> | |||
| )} | |||
| <Textarea | |||
| className={` | |||
| block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none | |||
| ${visionConfig?.enabled && 'pl-12'} | |||
| `} | |||
| value={query} | |||
| onChange={handleContentChange} | |||
| onKeyUp={handleKeyUp} | |||
| onKeyDown={handleKeyDown} | |||
| onPaste={onPaste} | |||
| onDragEnter={onDragEnter} | |||
| onDragLeave={onDragLeave} | |||
| onDragOver={onDragOver} | |||
| onDrop={onDrop} | |||
| autoSize | |||
| /> | |||
| </div> | |||
| <div className="absolute bottom-2 right-2 flex items-center h-8"> | |||
| <div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div> | |||
| { | |||
| @@ -440,6 +444,9 @@ const Chat: FC<IChatProps> = ({ | |||
| /> | |||
| )} | |||
| </div> | |||
| {customDisclaimer && <div className='text-xs text-gray-500 mt-1 text-center'> | |||
| {customDisclaimer} | |||
| </div>} | |||
| </div> | |||
| )} | |||
| </div> | |||
| @@ -31,6 +31,7 @@ export type ConfigParams = { | |||
| prompt_public: boolean | |||
| copyright: string | |||
| privacy_policy: string | |||
| custom_disclaimer: string | |||
| icon: string | |||
| icon_background: string | |||
| } | |||
| @@ -46,8 +47,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| const { notify } = useToastContext() | |||
| const [isShowMore, setIsShowMore] = useState(false) | |||
| const { icon, icon_background } = appInfo | |||
| const { title, description, copyright, privacy_policy, default_language } = appInfo.site | |||
| const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy }) | |||
| const { title, description, copyright, privacy_policy, custom_disclaimer, default_language } = appInfo.site | |||
| const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer }) | |||
| const [language, setLanguage] = useState(default_language) | |||
| const [saveLoading, setSaveLoading] = useState(false) | |||
| const { t } = useTranslation() | |||
| @@ -56,7 +57,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| const [emoji, setEmoji] = useState({ icon, icon_background }) | |||
| useEffect(() => { | |||
| setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy }) | |||
| setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer }) | |||
| setLanguage(default_language) | |||
| setEmoji({ icon, icon_background }) | |||
| }, [appInfo]) | |||
| @@ -81,6 +82,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| prompt_public: false, | |||
| copyright: inputInfo.copyright, | |||
| privacy_policy: inputInfo.privacyPolicy, | |||
| custom_disclaimer: inputInfo.customDisclaimer, | |||
| icon: emoji.icon, | |||
| icon_background: emoji.icon_background, | |||
| } | |||
| @@ -161,6 +163,13 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| onChange={onChange('privacyPolicy')} | |||
| placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string} | |||
| /> | |||
| <div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.customDisclaimer`)}</div> | |||
| <p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p> | |||
| <input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`} | |||
| value={inputInfo.customDisclaimer} | |||
| onChange={onChange('customDisclaimer')} | |||
| placeholder={t(`${prefixSettings}.more.customDisclaimerPlaceholder`) as string} | |||
| /> | |||
| </>} | |||
| <div className='mt-10 flex justify-end'> | |||
| <Button className='mr-2 flex-shrink-0 !text-sm' onClick={onHide}>{t('common.operation.cancel')}</Button> | |||
| @@ -14,6 +14,7 @@ import type { | |||
| VisionConfig, | |||
| } from '../types' | |||
| import { TransferMethod } from '../types' | |||
| import { useChatWithHistoryContext } from '../chat-with-history/context' | |||
| import TooltipPlus from '@/app/components/base/tooltip-plus' | |||
| import { ToastContext } from '@/app/components/base/toast' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| @@ -40,6 +41,7 @@ const ChatInput: FC<ChatInputProps> = ({ | |||
| speechToTextConfig, | |||
| onSend, | |||
| }) => { | |||
| const { appData } = useChatWithHistoryContext() | |||
| const { t } = useTranslation() | |||
| const { notify } = useContext(ToastContext) | |||
| const [voiceInputShow, setVoiceInputShow] = useState(false) | |||
| @@ -127,101 +129,106 @@ const ChatInput: FC<ChatInputProps> = ({ | |||
| ) | |||
| return ( | |||
| <div className='relative'> | |||
| <div | |||
| className={` | |||
| p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto | |||
| ${isDragActive && 'border-primary-600'} | |||
| `} | |||
| > | |||
| { | |||
| visionConfig?.enabled && ( | |||
| <> | |||
| <div className='absolute bottom-2 left-2 flex items-center'> | |||
| <ChatImageUploader | |||
| settings={visionConfig} | |||
| onUpload={onUpload} | |||
| disabled={files.length >= visionConfig.number_limits} | |||
| /> | |||
| <div className='mx-1 w-[1px] h-4 bg-black/5' /> | |||
| </div> | |||
| <div className='pl-[52px]'> | |||
| <ImageList | |||
| list={files} | |||
| onRemove={onRemove} | |||
| onReUpload={onReUpload} | |||
| onImageLinkLoadSuccess={onImageLinkLoadSuccess} | |||
| onImageLinkLoadError={onImageLinkLoadError} | |||
| /> | |||
| </div> | |||
| </> | |||
| ) | |||
| } | |||
| <Textarea | |||
| <> | |||
| <div className='relative'> | |||
| <div | |||
| className={` | |||
| block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none | |||
| ${visionConfig?.enabled && 'pl-12'} | |||
| p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto | |||
| ${isDragActive && 'border-primary-600'} mb-2 | |||
| `} | |||
| value={query} | |||
| onChange={handleContentChange} | |||
| onKeyUp={handleKeyUp} | |||
| onKeyDown={handleKeyDown} | |||
| onPaste={onPaste} | |||
| onDragEnter={onDragEnter} | |||
| onDragLeave={onDragLeave} | |||
| onDragOver={onDragOver} | |||
| onDrop={onDrop} | |||
| autoSize | |||
| /> | |||
| <div className='absolute bottom-[7px] right-2 flex items-center h-8'> | |||
| <div className='flex items-center px-1 h-5 rounded-md bg-gray-100 text-xs font-medium text-gray-500'> | |||
| {query.trim().length} | |||
| </div> | |||
| > | |||
| { | |||
| query | |||
| ? ( | |||
| <div className='flex justify-center items-center ml-2 w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => setQuery('')}> | |||
| <XCircle className='w-4 h-4 text-[#98A2B3]' /> | |||
| visionConfig?.enabled && ( | |||
| <> | |||
| <div className='absolute bottom-2 left-2 flex items-center'> | |||
| <ChatImageUploader | |||
| settings={visionConfig} | |||
| onUpload={onUpload} | |||
| disabled={files.length >= visionConfig.number_limits} | |||
| /> | |||
| <div className='mx-1 w-[1px] h-4 bg-black/5' /> | |||
| </div> | |||
| ) | |||
| : speechToTextConfig?.enabled | |||
| <div className='pl-[52px]'> | |||
| <ImageList | |||
| list={files} | |||
| onRemove={onRemove} | |||
| onReUpload={onReUpload} | |||
| onImageLinkLoadSuccess={onImageLinkLoadSuccess} | |||
| onImageLinkLoadError={onImageLinkLoadError} | |||
| /> | |||
| </div> | |||
| </> | |||
| ) | |||
| } | |||
| <Textarea | |||
| className={` | |||
| block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none | |||
| ${visionConfig?.enabled && 'pl-12'} | |||
| `} | |||
| value={query} | |||
| onChange={handleContentChange} | |||
| onKeyUp={handleKeyUp} | |||
| onKeyDown={handleKeyDown} | |||
| onPaste={onPaste} | |||
| onDragEnter={onDragEnter} | |||
| onDragLeave={onDragLeave} | |||
| onDragOver={onDragOver} | |||
| onDrop={onDrop} | |||
| autoSize | |||
| /> | |||
| <div className='absolute bottom-[7px] right-2 flex items-center h-8'> | |||
| <div className='flex items-center px-1 h-5 rounded-md bg-gray-100 text-xs font-medium text-gray-500'> | |||
| {query.trim().length} | |||
| </div> | |||
| { | |||
| query | |||
| ? ( | |||
| <div | |||
| className='group flex justify-center items-center ml-2 w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer' | |||
| onClick={handleVoiceInputShow} | |||
| > | |||
| <Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' /> | |||
| <Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' /> | |||
| <div className='flex justify-center items-center ml-2 w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => setQuery('')}> | |||
| <XCircle className='w-4 h-4 text-[#98A2B3]' /> | |||
| </div> | |||
| ) | |||
| : null | |||
| : speechToTextConfig?.enabled | |||
| ? ( | |||
| <div | |||
| className='group flex justify-center items-center ml-2 w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer' | |||
| onClick={handleVoiceInputShow} | |||
| > | |||
| <Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' /> | |||
| <Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' /> | |||
| </div> | |||
| ) | |||
| : null | |||
| } | |||
| <div className='mx-2 w-[1px] h-4 bg-black opacity-5' /> | |||
| {isMobile | |||
| ? sendBtn | |||
| : ( | |||
| <TooltipPlus | |||
| popupContent={ | |||
| <div> | |||
| <div>{t('common.operation.send')} Enter</div> | |||
| <div>{t('common.operation.lineBreak')} Shift Enter</div> | |||
| </div> | |||
| } | |||
| > | |||
| {sendBtn} | |||
| </TooltipPlus> | |||
| )} | |||
| </div> | |||
| { | |||
| voiceInputShow && ( | |||
| <VoiceInput | |||
| onCancel={() => setVoiceInputShow(false)} | |||
| onConverted={text => setQuery(text)} | |||
| /> | |||
| ) | |||
| } | |||
| <div className='mx-2 w-[1px] h-4 bg-black opacity-5' /> | |||
| {isMobile | |||
| ? sendBtn | |||
| : ( | |||
| <TooltipPlus | |||
| popupContent={ | |||
| <div> | |||
| <div>{t('common.operation.send')} Enter</div> | |||
| <div>{t('common.operation.lineBreak')} Shift Enter</div> | |||
| </div> | |||
| } | |||
| > | |||
| {sendBtn} | |||
| </TooltipPlus> | |||
| )} | |||
| </div> | |||
| { | |||
| voiceInputShow && ( | |||
| <VoiceInput | |||
| onCancel={() => setVoiceInputShow(false)} | |||
| onConverted={text => setQuery(text)} | |||
| /> | |||
| ) | |||
| } | |||
| </div> | |||
| </div> | |||
| {appData?.site?.custom_disclaimer && <div className='text-xs text-gray-500 mt-1 text-center'> | |||
| {appData.site.custom_disclaimer} | |||
| </div>} | |||
| </> | |||
| ) | |||
| } | |||
| @@ -929,6 +929,7 @@ const Main: FC<IMainProps> = ({ | |||
| image_file_size_limit: fileUploadConfigResponse ? fileUploadConfigResponse.image_file_size_limit : visionConfig.image_file_size_limit, | |||
| }} | |||
| allToolIcons={appMeta?.tool_icons || {}} | |||
| customDisclaimer={siteInfo.custom_disclaimer} | |||
| /> | |||
| </div> | |||
| </div>) | |||
| @@ -800,6 +800,7 @@ const Main: FC<IMainProps> = ({ | |||
| answerIcon={<LogoAvatar className='relative shrink-0' />} | |||
| visionConfig={visionConfig} | |||
| allToolIcons={appMeta?.tool_icons || {}} | |||
| customDisclaimer={siteInfo.custom_disclaimer} | |||
| /> | |||
| </div> | |||
| </div>) | |||
| @@ -267,6 +267,19 @@ const EditCustomCollectionModal: FC<Props> = ({ | |||
| className='w-full h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('tools.createTool.privacyPolicyPlaceholder') || ''} /> | |||
| </div> | |||
| <div> | |||
| <div className={fieldNameClassNames}>{t('tools.createTool.customDisclaimer')}</div> | |||
| <input | |||
| value={customCollection.custom_disclaimer} | |||
| onChange={(e) => { | |||
| const newCollection = produce(customCollection, (draft) => { | |||
| draft.custom_disclaimer = e.target.value | |||
| }) | |||
| setCustomCollection(newCollection) | |||
| }} | |||
| className='w-full h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('tools.createTool.customDisclaimerPlaceholder') || ''} /> | |||
| </div> | |||
| </div> | |||
| <div className={cn(isEdit ? 'justify-between' : 'justify-end', 'mt-2 shrink-0 flex py-4 px-6 rounded-b-[10px] bg-gray-50 border-t border-black/5')} > | |||
| { | |||
| @@ -89,6 +89,7 @@ export type CustomCollectionBackend = { | |||
| schema_type: string | |||
| schema: string | |||
| privacy_policy: string | |||
| custom_disclaimer: string | |||
| tools?: ParamItem[] | |||
| } | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Datenschutzrichtlinie', | |||
| privacyPolicyPlaceholder: 'Geben Sie den Link zur Datenschutzrichtlinie ein', | |||
| privacyPolicyTip: 'Hilft Besuchern zu verstehen, welche Daten die Anwendung sammelt, siehe Difys <privacyPolicyLink>Datenschutzrichtlinie</privacyPolicyLink>.', | |||
| customDisclaimer: 'Benutzerdefinierte Haftungsausschluss', | |||
| customDisclaimerPlaceholder: 'Geben Sie den benutzerdefinierten Haftungsausschluss-Text ein', | |||
| customDisclaimerTip: 'Der ben userdefinierte Haftungsausschluss-Text wird auf der Clientseite angezeigt und bietet zusätzliche Informationen über die Anwendung', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Datenschutzrichtlinie', | |||
| privacyPolicyPlaceholder: 'Bitte Datenschutzrichtlinie eingeben', | |||
| customDisclaimer: 'Benutzer Haftungsausschluss', | |||
| customDisclaimerPlaceholder: 'Bitte benutzerdefinierten Haftungsausschluss eingeben', | |||
| }, | |||
| test: { | |||
| title: 'Test', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Privacy Policy', | |||
| privacyPolicyPlaceholder: 'Enter the privacy policy link', | |||
| privacyPolicyTip: 'Helps visitors understand the data the application collects, see Dify\'s <privacyPolicyLink>Privacy Policy</privacyPolicyLink>.', | |||
| customDisclaimer: 'Custom Disclaimer', | |||
| customDisclaimerPlaceholder: 'Enter the custom disclaimer text', | |||
| customDisclaimerTip: 'Custom disclaimer text will be displayed on the client side, providing additional information about the application', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Privacy policy', | |||
| privacyPolicyPlaceholder: 'Please enter privacy policy', | |||
| customDisclaimer: 'Custom disclaimer', | |||
| customDisclaimerPlaceholder: 'Please enter custom disclaimer', | |||
| }, | |||
| test: { | |||
| title: 'Test', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Politique de confidentialité', | |||
| privacyPolicyPlaceholder: 'Entrez le lien de la politique de confidentialité', | |||
| privacyPolicyTip: 'Aide les visiteurs à comprendre les données collectées par l\'application, voir la <privacyPolicyLink>Politique de confidentialité</privacyPolicyLink> de Dify.', | |||
| customDisclaimer: 'Clause de non-responsabilité personnalisée', | |||
| customDisclaimerPlaceholder: 'Entrez le texte de la clause de non-responsabilité personnalisée', | |||
| customDisclaimerTip: 'Le texte de la clause de non-responsabilité personnalisée sera affiché côté client, fournissant des informations supplémentaires sur l\'application', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Politique de confidentialité', | |||
| privacyPolicyPlaceholder: 'Veuillez entrer la politique de confidentialité', | |||
| customDisclaimer: 'Clause de non-responsabilité personnalisée', | |||
| customDisclaimerPlaceholder: 'Entrez le texte de la clause de non-responsabilité personnalisée', | |||
| }, | |||
| test: { | |||
| title: 'Test', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'プライバシーポリシー', | |||
| privacyPolicyPlaceholder: 'プライバシーポリシーリンクを入力してください', | |||
| privacyPolicyTip: '訪問者がアプリケーションが収集するデータを理解し、Difyの<privacyPolicyLink>プライバシーポリシー</privacyPolicyLink>を参照できるようにします。', | |||
| customDisclaimer: 'カスタム免責事項', | |||
| customDisclaimerPlaceholder: '免責事項を入力してください', | |||
| customDisclaimerTip: 'アプリケーションの使用に関する免責事項を提供します。', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'プライバシーポリシー', | |||
| privacyPolicyPlaceholder: 'プライバシーポリシーを入力してください', | |||
| customDisclaimer: 'カスタム免責事項', | |||
| customDisclaimerPlaceholder: 'カスタム免責事項を入力してください', | |||
| }, | |||
| test: { | |||
| title: 'テスト', | |||
| @@ -55,6 +55,9 @@ const translation = { | |||
| privacyPolicyPlaceholder: 'Wprowadź link do polityki prywatności', | |||
| privacyPolicyTip: | |||
| 'Pomaga odwiedzającym zrozumieć, jakie dane zbiera aplikacja, zobacz <privacyPolicyLink>Politykę prywatności Dify</privacyPolicyLink>.', | |||
| customDisclaimer: 'Oświadczenie o ochronie danych', | |||
| customDisclaimerPlaceholder: 'Wprowadź oświadczenie o ochronie danych', | |||
| customDisclaimerTip: 'Niestandardowy tekst oświadczenia będzie wyświetlany po stronie klienta, dostarczając dodatkowych informacji o aplikacji.', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -73,6 +73,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Polityka prywatności', | |||
| privacyPolicyPlaceholder: 'Proszę wprowadzić politykę prywatności', | |||
| customDisclaimer: 'Oświadczenie niestandardowe', | |||
| customDisclaimerPlaceholder: 'Proszę wprowadzić oświadczenie niestandardowe', | |||
| }, | |||
| test: { | |||
| title: 'Test', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Política de Privacidade', | |||
| privacyPolicyPlaceholder: 'Insira o link da política de privacidade', | |||
| privacyPolicyTip: 'Ajuda os visitantes a entender os dados coletados pelo aplicativo, consulte a <privacyPolicyLink>Política de Privacidade</privacyPolicyLink> do Dify.', | |||
| customDisclaimer: 'Aviso Legal Personalizado', | |||
| customDisclaimerPlaceholder: 'Insira o texto do aviso legal', | |||
| customDisclaimerTip: 'O texto do aviso legal personalizado será exibido no lado do cliente, fornecendo informações adicionais sobre o aplicativo', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Política de Privacidade', | |||
| privacyPolicyPlaceholder: 'Digite a política de privacidade', | |||
| customDisclaimer: 'Aviso Personalizado', | |||
| customDisclaimerPlaceholder: 'Digite o aviso personalizado', | |||
| }, | |||
| test: { | |||
| title: 'Testar', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Політика конфіденційності', | |||
| privacyPolicyPlaceholder: 'Введіть посилання на політику конфіденційності', | |||
| privacyPolicyTip: 'Допомагає відвідувачам зрозуміти дані, зібрані додатком, див. <privacyPolicyLink>Політику конфіденційності</privacyPolicyLink> Dify.', | |||
| customDisclaimer: 'Відмова від відповідальності', | |||
| customDisclaimerPlaceholder: 'Введіть відмову від відповідальності', | |||
| customDisclaimerTip: 'Відображається на клієнтському боці, щоб визначити відповідальність за використання додатка', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -70,6 +70,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Політика конфіденційності', | |||
| privacyPolicyPlaceholder: 'Введіть політику конфіденційності', | |||
| customDisclaimer: 'Власний відомості', | |||
| customDisclaimerPlaceholder: 'Введіть власні відомості', | |||
| }, | |||
| test: { | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: 'Chính sách bảo mật', | |||
| privacyPolicyPlaceholder: 'Nhập liên kết chính sách bảo mật', | |||
| privacyPolicyTip: 'Giúp khách truy cập hiểu được dữ liệu mà ứng dụng thu thập, xem <privacyPolicyLink>Chính sách bảo mật</privacyPolicyLink> của Dify.', | |||
| customDisclaimer: 'Tùy chỉnh từ chối trách nhiệm', | |||
| customDisclaimerPlaceholder: 'Nhập liên kết từ chối trách nhiệm', | |||
| customDisclaimerTip: 'Liên kết này sẽ được hiển thị ở phía máy khách, cung cấp thông tin về trách nhiệm của ứng dụng', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: 'Chính sách bảo mật', | |||
| privacyPolicyPlaceholder: 'Vui lòng nhập chính sách bảo mật', | |||
| customDisclaimer: 'Tuyên bố Tùy chỉnh', | |||
| customDisclaimerPlaceholder: 'Vui lòng nhập tuyên bố tùy chỉnh', | |||
| }, | |||
| test: { | |||
| title: 'Kiểm tra', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: '隐私政策', | |||
| privacyPolicyPlaceholder: '请输入隐私政策链接', | |||
| privacyPolicyTip: '帮助访问者了解该应用收集的数据,可参考 Dify 的<privacyPolicyLink>隐私政策</privacyPolicyLink>。', | |||
| customDisclaimer: '自定义免责声明', | |||
| customDisclaimerPlaceholder: '请输入免责声明', | |||
| customDisclaimerTip: '在应用中展示免责声明,可用于告知用户 AI 的局限性。', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: '隐私协议', | |||
| privacyPolicyPlaceholder: '请输入隐私协议', | |||
| customDisclaimer: '自定义免责声明', | |||
| customDisclaimerPlaceholder: '请输入自定义免责声明', | |||
| }, | |||
| test: { | |||
| title: '测试', | |||
| @@ -50,6 +50,9 @@ const translation = { | |||
| privacyPolicy: '隱私政策', | |||
| privacyPolicyPlaceholder: '請輸入隱私政策連結', | |||
| privacyPolicyTip: '幫助訪問者瞭解該應用收集的資料,可參考 Dify 的<privacyPolicyLink>隱私政策</privacyPolicyLink>。', | |||
| customDisclaimer: '自定義免責聲明', | |||
| customDisclaimerPlaceholder: '請輸入免責聲明', | |||
| customDisclaimerTip: '客製化的免責聲明文字將在客戶端顯示,提供有關應用程式的額外資訊。', | |||
| }, | |||
| }, | |||
| embedded: { | |||
| @@ -71,6 +71,8 @@ const translation = { | |||
| }, | |||
| privacyPolicy: '隱私協議', | |||
| privacyPolicyPlaceholder: '請輸入隱私協議', | |||
| customDisclaimer: '自定義免責聲明', | |||
| customDisclaimerPlaceholder: '請輸入自定義免責聲明', | |||
| }, | |||
| test: { | |||
| title: '測試', | |||
| @@ -16,6 +16,7 @@ export type App = { | |||
| description: string | |||
| copyright: string | |||
| privacy_policy: string | null | |||
| custom_disclaimer: string | null | |||
| category: AppCategory | |||
| position: number | |||
| is_listed: boolean | |||
| @@ -18,6 +18,7 @@ export type SiteInfo = { | |||
| prompt_public?: boolean | |||
| copyright?: string | |||
| privacy_policy?: string | |||
| custom_disclaimer?: string | |||
| } | |||
| export type AppMeta = { | |||
| @@ -269,6 +269,8 @@ export type SiteConfig = { | |||
| copyright: string | |||
| /** Privacy Policy */ | |||
| privacy_policy: string | |||
| /** Custom Disclaimer */ | |||
| custom_disclaimer: string | |||
| icon: string | |||
| icon_background: string | |||