├── .github ├── hooks │ └── pre-commit └── workflows │ └── default.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── backend ├── app.db ├── app │ ├── controllers │ │ ├── __init__.py │ │ ├── app.py │ │ ├── common.py │ │ ├── pay_pro.py │ │ ├── plugine_api.py │ │ ├── requirement.py │ │ ├── setting.py │ │ ├── step_api.py │ │ ├── step_code.py │ │ ├── step_devops.py │ │ ├── step_requirement.py │ │ ├── step_subtask.py │ │ ├── tenant_pro.py │ │ ├── tencent_pro.py │ │ ├── user.py │ │ └── workspace.py │ ├── extensions.py │ ├── flask_ext.py │ ├── models │ │ ├── __init__.py │ │ ├── application.py │ │ ├── application_service.py │ │ ├── application_service_lib.py │ │ ├── async_task.py │ │ ├── async_task_record.py │ │ ├── repo_pro.py │ │ ├── requirement.py │ │ ├── requirement_memory_pro.py │ │ ├── setting.py │ │ ├── setting_basic.py │ │ ├── setting_interface.py │ │ ├── setting_pro.py │ │ ├── sys_lib.py │ │ ├── tenant_bill_pro.py │ │ ├── tenant_cd_config_pro.py │ │ ├── tenant_ci_config_pro.py │ │ ├── tenant_git_config_pro.py │ │ ├── tenant_pro.py │ │ ├── tenant_user_pro.py │ │ ├── user.py │ │ └── user_pro.py │ └── pkgs │ │ ├── __init__.py │ │ ├── analyzer_code_exception.py │ │ ├── devops │ │ ├── __init__.py │ │ ├── cd.py │ │ ├── cd_aliyun.py │ │ ├── cd_awsecs.py │ │ ├── cd_interface.py │ │ ├── cd_local.py │ │ ├── devops.py │ │ ├── devops_github.py │ │ ├── devops_gitlab.py │ │ ├── devops_interface.py │ │ ├── devops_local.py │ │ ├── devops_pro.py │ │ ├── git_tools.py │ │ ├── local_tools.py │ │ ├── local_tools_base.py │ │ ├── local_tools_interface.py │ │ └── local_tools_pro.py │ │ ├── knowledge │ │ ├── __init__.py │ │ ├── app_info.py │ │ ├── app_info_basic.py │ │ ├── app_info_interface.py │ │ └── app_info_pro.py │ │ ├── prompt │ │ ├── __init__.py │ │ ├── api_basic.py │ │ ├── api_interface.py │ │ ├── api_pro.py │ │ ├── code_basic.py │ │ ├── code_interface.py │ │ ├── code_pro.py │ │ ├── prompt.py │ │ ├── requirement_basic.py │ │ ├── requirement_interface.py │ │ ├── requirement_pro.py │ │ ├── subtask_basic.py │ │ ├── subtask_interface.py │ │ ├── subtask_java_pro.py │ │ ├── subtask_pro.py │ │ ├── subtask_python_pro.py │ │ └── subtask_vue_pro.py │ │ ├── scheduler │ │ ├── __init__.py │ │ └── scheduler.py │ │ └── tools │ │ ├── __init__.py │ │ ├── file_tool.py │ │ ├── i18b.py │ │ ├── llm.py │ │ ├── llm_basic.py │ │ ├── llm_interface.py │ │ ├── llm_pro.py │ │ ├── storage.py │ │ └── utils_tool.py ├── config.py ├── run.py └── test.py ├── build.sh ├── db ├── database.db └── script.py.mako ├── docs ├── CONFIG_CHANGE.md ├── CONTACT.md ├── CONTRIBUTING.md ├── DOCUMENT.md ├── DOCUMENT_CN.md ├── README_CN.md ├── README_JA.md └── files │ ├── WeChat-zhushou.png │ ├── WeChat-微信-green.svg │ ├── WeChat.jpg │ ├── ci.png │ ├── demo-adduser-en.jpeg │ ├── demo-adduser.jpeg │ ├── demo-gptmeeting.jpeg │ ├── demo-jiqiren.jpeg │ ├── document-English-blue.svg │ ├── intro-flow-en.png │ ├── intro-flow-simple.png │ ├── intro-flow.png │ ├── ドキュメント-日本語-blue.svg │ ├── 官网-企业版-purple.svg │ └── 文档-中文版-blue.svg ├── env.yaml.tpl ├── frontend ├── Semantic UI_files │ ├── analytics.js │ ├── book.png │ ├── button.e7f9415a2e000feaab02c86dd5802747.js │ ├── button.less │ ├── devices.png │ ├── docs.css │ ├── docs.js │ ├── easing.min.js │ ├── elyse.png │ ├── eve.png │ ├── github-btn(1).html │ ├── github-btn.html │ ├── heart.png │ ├── highlight.min.js │ ├── home.css │ ├── home.js │ ├── jquery.min.js │ ├── js │ ├── kristy.png │ ├── lab.png │ ├── lena.png │ ├── less.min.js │ ├── live.js │ ├── logo.png │ ├── manifest.jsonp │ ├── mark.png │ ├── matthew.png │ ├── mobile.png │ ├── molly.png │ ├── paint.png │ ├── rachel.png │ ├── rocket.png │ ├── rtl.css │ ├── saved_resource.html │ ├── semantic-ui │ ├── semantic.min.css │ ├── semantic.min.js │ ├── theming.png │ ├── toolbox.png │ ├── tweet_button.2b2d73daf636805223fb11d48f3e94f7.en.html │ ├── widget_iframe.2b2d73daf636805223fb11d48f3e94f7.html │ └── widgets.js ├── app.html ├── favicon.ico ├── i_clouderwork.html ├── index.html ├── privacy.html ├── requirement.html ├── setting.html ├── static │ ├── css │ │ ├── codemirror.min.css │ │ ├── darcula.min.css │ │ ├── semantic.min.css │ │ ├── style.css │ │ └── themes │ │ │ └── default │ │ │ └── assets │ │ │ ├── fonts │ │ │ ├── brand-icons.woff2 │ │ │ ├── icons.woff2 │ │ │ └── outline-icons.woff2 │ │ │ └── images │ │ │ └── flags.png │ ├── image │ │ ├── WeChat-zhushou.png │ │ ├── WeChat.jpg │ │ ├── i_cloudwork_logo.png │ │ ├── i_cloudwork_show.png │ │ ├── i_cloudwork_wechat.png │ │ ├── index_chanpinjietu.png │ │ ├── index_demo_chatbot.png │ │ ├── index_demo_crud.png │ │ ├── index_demo_more.png │ │ ├── index_header.jpeg │ │ ├── logo.png │ │ ├── models.png │ │ ├── role_op.jpeg │ │ ├── role_op的副本.jpeg │ │ ├── role_pm.jpeg │ │ ├── role_pm的副本.jpeg │ │ ├── role_qa.jpeg │ │ ├── role_qa的副本.jpeg │ │ ├── role_rd.jpeg │ │ ├── role_rd的副本.jpeg │ │ ├── role_tl.jpeg │ │ └── role_tl的副本.jpeg │ └── js │ │ ├── app.js │ │ ├── codemirror.min.js │ │ ├── coder.js │ │ ├── diff.min.js │ │ ├── haxe.min.js │ │ ├── index.js │ │ ├── jquery.min.js │ │ ├── marked.min.js │ │ ├── requirement.js │ │ ├── semantic.min.js │ │ ├── setting.js │ │ ├── tenant.js │ │ └── user.js ├── task.html ├── tenant.html ├── tenant_detail.html ├── tenant_new.html ├── user_changepassword.html ├── user_login.html └── user_register.html ├── i18n ├── README.md ├── controllers.pot ├── en │ └── LC_MESSAGES │ │ ├── controllers.mo │ │ ├── frontend.mo │ │ └── prompt.mo ├── en_controllers.po ├── en_controllers.po~ ├── en_frontend.po ├── en_frontend.po~ ├── en_prompt.po ├── frontend.pot ├── prompt.pot ├── zh │ └── LC_MESSAGES │ │ ├── controllers.mo │ │ ├── frontend.mo │ │ └── prompt.mo ├── zh_controllers.po ├── zh_controllers.po~ ├── zh_frontend.po ├── zh_frontend.po~ └── zh_prompt.po ├── requirements.txt ├── run.bat ├── run.sh └── templete ├── ai_code_analyzer_0.0.2 ├── bootstrap │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map ├── content-script.js ├── css │ └── sidepanel.css ├── images │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-48.png │ └── logo.png ├── js │ └── jquery.js ├── manifest.json ├── readme.assets │ ├── image-20231108140756357.png │ ├── image-20231108140947012.png │ ├── image-20231108140949410.png │ ├── image-20231108141054506.png │ ├── image-20231108141159271.png │ ├── image-20231108143057951.png │ ├── image-20231108143242430.png │ ├── image-20231108143405016.png │ ├── image-20231108143419728.png │ ├── image-20231108143638495.png │ ├── image-20231108143755396.png │ └── image-20231108491651651.png ├── readme.md ├── service-worker.js ├── sidepanel.html └── sidepanel.js └── ai_code_analyzer_0.0.3 ├── bootstrap ├── css │ ├── bootstrap-grid.css │ ├── bootstrap-grid.css.map │ ├── bootstrap-grid.min.css │ ├── bootstrap-grid.min.css.map │ ├── bootstrap-reboot.css │ ├── bootstrap-reboot.css.map │ ├── bootstrap-reboot.min.css │ ├── bootstrap-reboot.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ └── bootstrap.min.css.map └── js │ ├── bootstrap.bundle.js │ ├── bootstrap.bundle.js.map │ ├── bootstrap.bundle.min.js │ ├── bootstrap.bundle.min.js.map │ ├── bootstrap.js │ ├── bootstrap.js.map │ ├── bootstrap.min.js │ └── bootstrap.min.js.map ├── content-script.js ├── css └── sidepanel.css ├── images ├── icon-128.png ├── icon-16.png ├── icon-48.png └── logo.png ├── js └── jquery.js ├── lock.js ├── manifest.json ├── service-worker.js ├── sidepanel.html └── sidepanel.js /.github/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check for updates to files ending with "pro.py" 4 | if git diff --cached --name-only | grep -E 'pro.py'; then 5 | echo "HOOK Error: You have made updates to files ending with 'pro.py' it's forbidden." 6 | echo "Please remove or revert these changes before committing." 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /.github/workflows/default.yaml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "master" branch 8 | push: 9 | branches: [ "master" ] 10 | pull_request: 11 | branches: [ "master" ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Runs a set of commands using the runners shell 29 | - name: Get pwd 30 | run: | 31 | pwd 32 | ls -alh 33 | - name: Run Docker Build Script 34 | env: 35 | DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 36 | DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} 37 | DOCKER_REPO: ${{ secrets.DOCKERHUB_REPO }} 38 | run: | 39 | chmod +x build.sh 40 | ./build.sh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nohup.out* 2 | *__pycache__* 3 | *.vscode* 4 | backend/env.yaml 5 | env.yaml 6 | .DS_Store 7 | workspace 8 | init.sh 9 | db/versions/* 10 | db/env.py 11 | db/README.md 12 | db/database_pro.db 13 | alembic.ini* 14 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | ## PYTHON 6 | 7 | RUN apt update 8 | RUN apt install software-properties-common -y 9 | RUN add-apt-repository ppa:deadsnakes/ppa -y && \ 10 | apt update 11 | RUN apt install python3.10 -y 12 | RUN apt install python3.10-dev python3.10-venv python3-pip -y 13 | 14 | # Utils 15 | RUN apt install -y curl \ 16 | lsof \ 17 | git \ 18 | sqlite3 19 | 20 | COPY ./ /app 21 | 22 | RUN chmod +x /app/run.sh 23 | 24 | RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r /app/requirements.txt 25 | 26 | WORKDIR /app 27 | EXPOSE 8080 8081 28 | 29 | ENTRYPOINT ["/app/run.sh"] 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DevOpsGPT Open Source License 2 | 3 | The DevOpsGPT project uses the Apache License 2.0, along with an additional protocol to prevent direct competition with DevOpsGPT and KuafuAI services. 4 | 5 | As a contributor, you should agree that your contributed code: 6 | a. Might be subject to a more permissive open source license in the future. 7 | b. Can be used for commercial purposes. 8 | 9 | With the Apache License 2.0 License, and this supplementary agreement, anyone can freely use, modify, and distribute DevOpsGPT, provided that: If you wish to use DevOpsGPT for commercial and closed source SaaS services, please contact us for authorization. 10 | 11 | The interactive design of this product is protected by appearance patent. 12 | 13 | © 2023 KuafuAI, Inc. 14 | 15 | ------------------------ 16 | 17 | Licensed under the Apache License, Version 2.0 (the "License"); 18 | you may not use this file except in compliance with the License. 19 | You may obtain a copy of the License at 20 | 21 | http://www.apache.org/licenses/LICENSE-2.0 22 | 23 | Unless required by applicable law or agreed to in writing, software 24 | distributed under the License is distributed on an "AS IS" BASIS, 25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | See the License for the specific language governing permissions and 27 | limitations under the License. 28 | -------------------------------------------------------------------------------- /backend/app.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app.db -------------------------------------------------------------------------------- /backend/app/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from .user import bp as user_bp 2 | from .requirement import bp as requirement_bp 3 | from .workspace import bp as workspace_bp 4 | from .app import bp as app_bp 5 | from .step_devops import bp as step_devops_bp 6 | from .step_subtask import bp as step_subtask_bp 7 | from .step_code import bp as step_code_bp 8 | from .step_api import bp as step_api_bp 9 | from .step_requirement import bp as step_requirement_bp 10 | from .setting import bp as setting_bp 11 | from .tenant_pro import bp as tenant_bp 12 | from .pay_pro import bp as pay_bp 13 | from .plugine_api import bp as plugine_bp 14 | from .tencent_pro import bp as tencent_bp 15 | 16 | def register_controllers(app): 17 | app.register_blueprint(user_bp) 18 | app.register_blueprint(requirement_bp) 19 | app.register_blueprint(workspace_bp) 20 | app.register_blueprint(app_bp) 21 | app.register_blueprint(step_requirement_bp) 22 | app.register_blueprint(step_api_bp) 23 | app.register_blueprint(step_subtask_bp) 24 | app.register_blueprint(step_code_bp) 25 | app.register_blueprint(step_devops_bp) 26 | app.register_blueprint(setting_bp) 27 | app.register_blueprint(tenant_bp) 28 | app.register_blueprint(pay_bp) 29 | app.register_blueprint(plugine_bp) 30 | app.register_blueprint(tencent_bp) -------------------------------------------------------------------------------- /backend/app/controllers/app.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | 3 | from app.controllers.common import json_response 4 | from flask import Blueprint 5 | from app.pkgs.tools.i18b import getI18n 6 | from app.pkgs.knowledge.app_info import analyzeService 7 | from app.models.application import Application 8 | from app.models.application_service import ApplicationService 9 | from app.models.application_service_lib import ApplicationServiceLib 10 | from app.models.tenant_pro import Tenant 11 | from app.pkgs.tools import storage 12 | from config import GRADE 13 | 14 | bp = Blueprint('app', __name__, url_prefix='/app') 15 | 16 | 17 | @bp.route('/create', methods=['POST']) 18 | @json_response 19 | def add(): 20 | _ = getI18n("controllers") 21 | name = request.json.get('app_name') 22 | tenant_id = storage.get("tenant_id") 23 | app_id = request.json.get('app_id') 24 | default_source_branch = request.json.get('app_default_source_branch') 25 | default_target_branch = request.json.get('app_default_target_branch') 26 | description = request.json.get('app_description') 27 | services = request.json.get('service') 28 | cd_config = request.json.get('app_cd_config') 29 | ci_config = request.json.get('app_ci_config') 30 | git_config = request.json.get('app_git_config') 31 | creater = storage.get("username") 32 | 33 | try: 34 | if app_id: 35 | app = Application.update_application(app_id, tenant_id=tenant_id, name=name, description=description, default_source_branch=default_source_branch, default_target_branch=default_target_branch, cd_config=cd_config, ci_config=ci_config, git_config=git_config) 36 | ApplicationService.delete_service_by_app_id(app_id) 37 | appID = app_id 38 | else: 39 | app, success = Application.create(tenant_id, creater, name, description, default_source_branch, default_target_branch, git_config, ci_config, cd_config) 40 | if not success: 41 | raise Exception(app) 42 | appID = app.app_id 43 | 44 | for service in services: 45 | if "service_name" in service: 46 | newService = ApplicationService.create_service(appID, service["service_name"], service["service_git_path"], service["service_workflow"], service["service_role"], service["service_language"], service["service_framework"], service["service_database"], service["service_api_type"], service["service_api_location"], service["service_container_name"], service["service_container_group"], service["service_region"], '', service["service_security_group"], service["service_cd_subnet"], service["service_struct_cache"], '', service["service_service_type"], service["service_cd_subnet2"], service["service_cd_execution_role_arn"], service["service_cd_vpc"]) 47 | 48 | ApplicationServiceLib.create_libs(newService.service_id, service["service_libs_name"]) 49 | 50 | return {'success': appID} 51 | except Exception as e: 52 | raise Exception(_("Failed to add an application.")) 53 | 54 | 55 | @bp.route('/get', methods=['GET']) 56 | @json_response 57 | def getAll(): 58 | _ = getI18n("controllers") 59 | tenantID = storage.get("tenant_id") 60 | appID = request.args.get('app_id') 61 | 62 | try: 63 | apps = Application.get_all_application(tenantID, appID) 64 | 65 | return {'apps': apps} 66 | except Exception as e: 67 | raise Exception(_("Failed to get applications.")) 68 | 69 | @bp.route('/get_tpl', methods=['GET']) 70 | @json_response 71 | def get_tpl(): 72 | _ = getI18n("controllers") 73 | tenantID = 0 74 | appID = request.args.get('app_id') 75 | 76 | try: 77 | apps = Application.get_all_application(tenantID, appID) 78 | 79 | return {'apps': apps} 80 | except Exception as e: 81 | raise Exception(_("Failed to get applications.")) 82 | 83 | @bp.route('/analyze_service', methods=['POST']) 84 | @json_response 85 | def analyze_service(): 86 | _ = getI18n("controllers") 87 | tenantID = storage.get("tenant_id") 88 | gitPath = request.json.get('service_git_path') 89 | 90 | if len(gitPath) < 1: 91 | raise Exception(_("Failed to analysis applications.")) 92 | 93 | if GRADE != "base": 94 | passed, msg = Tenant.check_quota(tenantID, checkPlus=False, checkTask=False, checkCodePower=True) 95 | if not passed: 96 | raise Exception(msg) 97 | 98 | info, success = analyzeService(tenantID, gitPath) 99 | if not success: 100 | raise Exception(_("Failed to analysis applications.")+"(AI 自动导入已有代码库新建应用功能,目前只支持Java和python语言,其它语言可通过模板创建。 Currently, AI import existing code project only Java and python languages are supported. For other languages, use Template creation )") 101 | 102 | return info 103 | -------------------------------------------------------------------------------- /backend/app/controllers/common.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import traceback 3 | from flask import jsonify 4 | from flask_limiter import RateLimitExceeded 5 | from app.pkgs.analyzer_code_exception import AnalyzerCodeException, AnalyzerCodeProcessException 6 | 7 | 8 | def json_response(func): 9 | @wraps(func) # Preserve the original function's name and docstring 10 | def decorated_function(*args, **kwargs): 11 | try: 12 | result = func(*args, **kwargs) 13 | response = { 14 | 'success': True, 15 | 'data': result 16 | } 17 | except RateLimitExceeded as e: 18 | response = { 19 | 'success': False, 20 | 'data': { 21 | 'message': str(e) 22 | } 23 | } 24 | except AnalyzerCodeException as e: 25 | response = { 26 | 'success': False, 27 | 'data': { 28 | 'message': str(e), 29 | 'error_code': e.error_code 30 | } 31 | } 32 | except AnalyzerCodeProcessException as e: 33 | response = { 34 | 'success': False, 35 | 'data': { 36 | 'message': str(e), 37 | 'error_code': e.error_code, 38 | 'task_no': e.task_no, 39 | 'repo': e.repo 40 | } 41 | } 42 | except Exception as e: 43 | response = { 44 | 'success': False, 45 | 'error': str(e) 46 | } 47 | traceback.print_exc() 48 | 49 | return jsonify(response) 50 | 51 | return decorated_function 52 | -------------------------------------------------------------------------------- /backend/app/controllers/pay_pro.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | bp = Blueprint('pay', __name__, url_prefix='/pay') 4 | 5 | def test(): 6 | pass -------------------------------------------------------------------------------- /backend/app/controllers/plugine_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | from app.controllers.common import json_response 3 | from app.flask_ext import limiter_ip 4 | from flask import Blueprint, request 5 | from app.pkgs.tools.i18b import getI18n 6 | from app.models.async_task import AsyncTask 7 | from app.pkgs.analyzer_code_exception import AnalyzerCodeException, AnalyzerCodeProcessException 8 | 9 | bp = Blueprint('plugine', __name__, url_prefix='/plugine') 10 | 11 | 12 | @bp.route('/repo_analyzer', methods=['GET']) 13 | @json_response 14 | @limiter_ip.limit("1 per 5 second") 15 | def repo_analyzer_plugine(): 16 | _ = getI18n("controllers") 17 | 18 | ip = str(request.headers.get("X-Forwarded-For", '127.0.0.1')) 19 | type = request.args.get("type") 20 | repo = request.args.get("repo") 21 | if type is None or repo is None: 22 | raise Exception("param error") 23 | if len(type) == 0 or len(repo) == 0: 24 | raise Exception("param error") 25 | 26 | count = AsyncTask.get_today_analyzer_code_count(ip, AsyncTask.Search_Process_Key) 27 | if count > 0: 28 | process_task = AsyncTask.get_today_analyzer_code_list(ip, AsyncTask.Search_Process_Key) 29 | if process_task: 30 | content = json.loads(process_task.task_content) 31 | repo = content['repo'] 32 | task_no = process_task.token 33 | raise AnalyzerCodeProcessException("There are currently tasks being processed, please wait...", 1001, task_no, repo) 34 | raise AnalyzerCodeException("There are currently tasks being processed, please wait...", 1001) 35 | 36 | count = AsyncTask.get_today_analyzer_code_count(ip, AsyncTask.Search_Done_key) 37 | if count >= 3: 38 | raise AnalyzerCodeException("The analysis frequency for today has been used up. You can register for use on the platform", 3001) 39 | 40 | data = {"type": type, "repo": repo} 41 | 42 | task = AsyncTask.create_task(AsyncTask.Type_Analyzer_Code, type + ":" + repo, json.dumps(data), ip) 43 | if task: 44 | return {"task_no": task.token} 45 | else: 46 | raise AnalyzerCodeException("Server exception, please contact the administrator", 5001) 47 | 48 | 49 | @bp.route('/repo_analyzer_check', methods=['GET']) 50 | @json_response 51 | def repo_analyzer_check(): 52 | task_no = request.args.get("task_no") 53 | if task_no is None or len(task_no) == 0: 54 | raise Exception("param error") 55 | 56 | task = AsyncTask.get_task_by_token(task_no) 57 | if task: 58 | return {"task_no": task.token, "status": task.task_status, "message": task.task_status_message} 59 | else: 60 | raise Exception("查询数据不存在") 61 | -------------------------------------------------------------------------------- /backend/app/controllers/requirement.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, request 2 | from app.controllers.common import json_response 3 | from app.pkgs.tools.i18b import getI18n 4 | from app.models.requirement import Requirement 5 | from app.models.requirement_memory_pro import RequirementMemory 6 | from app.models.tenant_pro import Tenant 7 | from app.models.tenant_bill_pro import TenantBill 8 | from app.pkgs.tools import storage 9 | from config import REQUIREMENT_STATUS_NotStarted, GRADE 10 | 11 | bp = Blueprint('requirement', __name__, url_prefix='/requirement') 12 | 13 | 14 | @bp.route('/clear_up', methods=['GET']) 15 | @json_response 16 | def clear_up(): 17 | if GRADE != "base": 18 | tenant = Tenant.get_tenant_baseinfo_by_id(storage.get("tenant_id")) 19 | if tenant: 20 | tenant_name = tenant["name"] 21 | billing_type_name = tenant["billing_type_name"] 22 | code_power = TenantBill.get_total_codepower(storage.get("tenant_id")) 23 | else: 24 | tenant_name = "DevOpsGPT" 25 | code_power = '0' 26 | billing_type_name = "FREE" 27 | 28 | return {"username": storage.get("username"), "billing_type_name": billing_type_name, "tenant_name": tenant_name, "tenant_id": storage.get("tenant_id"), "code_power": code_power} 29 | 30 | 31 | @bp.route('/setup_app', methods=['POST']) 32 | @json_response 33 | def setup_app(): 34 | _ = getI18n("controllers") 35 | data = request.json 36 | appID = data['app_id'] 37 | sourceBranch = data['source_branch'] 38 | featureBranch = data['feature_branch'] 39 | username = storage.get("username") 40 | tenantID = storage.get("tenant_id") 41 | 42 | if GRADE != "base": 43 | passed, msg = Tenant.check_quota(tenantID) 44 | if not passed: 45 | raise Exception(msg) 46 | 47 | requirement = Requirement.create_requirement( 48 | tenantID, "New requirement", "New", appID, username, sourceBranch, featureBranch, REQUIREMENT_STATUS_NotStarted, 0, 0) 49 | 50 | if requirement.requirement_id: 51 | return Requirement.get_requirement_by_id(requirement.requirement_id, tenantID) 52 | else: 53 | raise Exception(_("Failed to set up app.")) 54 | 55 | 56 | @bp.route('/get', methods=['GET']) 57 | @json_response 58 | def get_all(): 59 | _ = getI18n("controllers") 60 | tenantID = storage.get("tenant_id") 61 | 62 | requirements = Requirement.get_all_requirements(tenantID, 1, 100) 63 | 64 | return {'requirements': requirements["requirements"]} 65 | 66 | 67 | @bp.route('/get_one', methods=['GET']) 68 | @json_response 69 | def get_one(): 70 | _ = getI18n("controllers") 71 | requirementID = request.args.get('requirement_id') 72 | tenantID = storage.get("tenant_id") 73 | 74 | requirement = Requirement.get_requirement_by_id(requirementID, tenantID) 75 | if not requirement: 76 | raise Exception(_("The task does not exist.")) 77 | 78 | memory = { 79 | "task_info": { 80 | "app_id": requirement["app_id"], 81 | "task_id": requirement["requirement_id"], 82 | "source_branch": requirement["default_source_branch"], 83 | "feature_branch": requirement["default_target_branch"] 84 | } 85 | } 86 | requirement["old_memory"] = memory 87 | 88 | if GRADE != "base": 89 | requirement["memory"] = RequirementMemory.get_all_requirement_memories( 90 | requirementID, 1) 91 | 92 | return requirement 93 | 94 | 95 | @bp.route('/update', methods=['POST']) 96 | @json_response 97 | def update(): 98 | _ = getI18n("controllers") 99 | data = request.json 100 | requirement_id = data['requirement_id'] 101 | update_data = data['data'] 102 | tenantID = storage.get("tenant_id") 103 | 104 | requirement = Requirement.update_requirement(requirement_id, tenantID, **update_data) 105 | 106 | if requirement.requirement_id: 107 | return Requirement.get_requirement_by_id(requirement.requirement_id, tenantID) 108 | else: 109 | raise Exception(_("Failed to set up app.")) 110 | -------------------------------------------------------------------------------- /backend/app/controllers/step_api.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | from app.pkgs.tools import storage 3 | from app.controllers.common import json_response 4 | from flask import Blueprint 5 | from app.pkgs.tools.i18b import getI18n 6 | from app.pkgs.prompt.prompt import clarifyAPI 7 | from app.pkgs.knowledge.app_info import getServiceSwagger 8 | from app.models.requirement import Requirement 9 | 10 | bp = Blueprint('step_api', __name__, url_prefix='/step_api') 11 | 12 | @bp.route('/clarify', methods=['POST']) 13 | @json_response 14 | def gen_interface_doc(): 15 | _ = getI18n("controllers") 16 | userPrompt = request.json.get('user_prompt') 17 | username = storage.get("username") 18 | requirementID = request.json.get('task_id') 19 | tenant_id = storage.get("tenant_id") 20 | 21 | # todo Use llm to determine which interface documents to adjust 22 | req = Requirement.get_requirement_by_id(requirementID, tenant_id) 23 | apiDoc, success = getServiceSwagger(req["app_id"], 0) 24 | 25 | Requirement.update_requirement(requirement_id=requirementID, tenant_id=tenant_id, original_requirement=userPrompt) 26 | 27 | msg, success = clarifyAPI(requirementID, userPrompt, apiDoc) 28 | 29 | if success: 30 | return {'message': msg} 31 | else: 32 | raise Exception(_("Failed to clarify API.")) -------------------------------------------------------------------------------- /backend/app/controllers/step_code.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | from app.pkgs.tools import storage 3 | from app.controllers.common import json_response 4 | from app.pkgs.tools.i18b import getI18n 5 | from app.pkgs.prompt.prompt import aiGenCode, aiMergeCode, aiCheckCode, aiFixError, aiReferenceRepair 6 | from app.pkgs.devops.local_tools import getFileContent 7 | from flask import Blueprint 8 | 9 | from app.pkgs.prompt.prompt import gen_write_code 10 | 11 | bp = Blueprint('step_code', __name__, url_prefix='/step_code') 12 | 13 | @bp.route('/edit_file_task', methods=['POST']) 14 | @json_response 15 | def edit_file_task(): 16 | _ = getI18n("controllers") 17 | newTask = request.json.get('new_task') 18 | newCode = request.json.get('new_code') 19 | fileTask = request.json.get('file_task') 20 | filePath = request.json.get('file_path') 21 | requirementID = request.json.get('task_id') 22 | 23 | re, success = aiGenCode(requirementID, fileTask, newTask, newCode, filePath) 24 | if not success: 25 | raise Exception(_("Failed to edit file with new task.")) 26 | 27 | return {'success': success, 'code': re["code"], 'reasoning': re["reasoning"]} 28 | 29 | @bp.route('/check_code', methods=['POST']) 30 | @json_response 31 | def check_file(): 32 | _ = getI18n("controllers") 33 | code = request.json.get('code') 34 | fileTask = request.json.get('fileTask') 35 | requirementID = request.json.get('task_id') 36 | filePath = request.json.get('file_path') 37 | step = request.json.get('step') 38 | service_name = request.json.get('service_name') 39 | 40 | if step: 41 | re, success = gen_write_code(requirementID, service_name, filePath, fileTask, step) 42 | else: 43 | re, success = aiCheckCode(requirementID, fileTask, code, filePath, service_name) 44 | if not success: 45 | raise Exception(_("Failed to check file.")) 46 | 47 | return {'success': success, 'code': re["code"], 'reasoning': re["reasoning"]} 48 | 49 | @bp.route('/merge_file', methods=['POST']) 50 | @json_response 51 | def merge_file(): 52 | _ = getI18n("controllers") 53 | baseCode = request.json.get('old_code') 54 | newCode = request.json.get('new_code') 55 | fileTask = request.json.get('file_task') 56 | userName = storage.get("username") 57 | requirementID = request.json.get('task_id') 58 | filePath = request.json.get('file_path') 59 | 60 | re, success = aiMergeCode(requirementID, fileTask, baseCode, newCode, filePath) 61 | if not success: 62 | raise Exception(_("Failed to merge old and new code.")) 63 | 64 | return {'success': success, 'code': re["code"], 'reasoning': re["reasoning"]} 65 | 66 | @bp.route('/reference_repair', methods=['POST']) 67 | @json_response 68 | def reference_repair(): 69 | _ = getI18n("controllers") 70 | fileTask = request.json.get('file_task') 71 | newCode = request.json.get('new_code') 72 | referenceFile = request.json.get('reference_file') 73 | repo = request.json.get('repo') 74 | userName = storage.get("username") 75 | requirementID = request.json.get('task_id') 76 | filePath = request.json.get('file_path') 77 | 78 | hasGitCode, referenceCode = getFileContent(referenceFile, repo) 79 | if not hasGitCode: 80 | raise Exception(_("Failed to reference repair no reference file found.")) 81 | 82 | re, success = aiReferenceRepair(requirementID, newCode, referenceCode, fileTask, filePath) 83 | if not success: 84 | raise Exception(_("Reference repair failed for unknown reasons.")) 85 | 86 | return {'success': success, 'code': re["code"], 'reasoning': re["reasoning"]} 87 | 88 | 89 | @bp.route('/fix_compile', methods=['POST']) 90 | @json_response 91 | def fix_compile(): 92 | code = request.json.get('code') 93 | solution = request.json.get('solution') 94 | requirementID = request.json.get('task_id') 95 | filePath = request.json.get('file_path') 96 | error_msg = request.json.get('error_msg') 97 | 98 | re, success = aiFixError(requirementID, error_msg, solution, code, filePath, "compile") 99 | reCode = re["code"] 100 | reason = re["reasoning"] 101 | 102 | return {'success': success, 'code': reCode, 'reasoning': reason} 103 | 104 | 105 | @bp.route('/fix_lint', methods=['POST']) 106 | @json_response 107 | def fix_lint(): 108 | code = request.json.get('code') 109 | solution = request.json.get('solution') 110 | requirementID = request.json.get('task_id') 111 | filePath = request.json.get('file_path') 112 | error_msg = request.json.get('error_msg') 113 | 114 | re, success = aiFixError(requirementID ,error_msg, solution, code, filePath, "lint") 115 | reCode = re["code"] 116 | reason = re["reasoning"] 117 | 118 | return {'success': success, 'code': reCode, 'reasoning': reason} -------------------------------------------------------------------------------- /backend/app/controllers/step_requirement.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | from app.pkgs.tools import storage 3 | from app.controllers.common import json_response 4 | from flask import Blueprint 5 | from app.pkgs.tools.i18b import getI18n 6 | from app.pkgs.prompt.prompt import clarifyRequirement 7 | from app.pkgs.knowledge.app_info import getAppArchitecture 8 | from app.models.requirement import Requirement 9 | from app.models.tenant_bill_pro import TenantBill 10 | from config import GRADE 11 | from config import REQUIREMENT_STATUS_InProgress 12 | 13 | bp = Blueprint('step_requirement', __name__, url_prefix='/step_requirement') 14 | 15 | @bp.route('/clarify', methods=['POST']) 16 | @json_response 17 | def clarify(): 18 | _ = getI18n("controllers") 19 | userPrompt = request.json.get('user_prompt') 20 | globalContext = request.json.get('global_context') 21 | userName = storage.get("username") 22 | requirementID = request.json.get('task_id') 23 | tenantID = storage.get("tenant_id") 24 | 25 | req = Requirement.get_requirement_by_id(requirementID, tenantID) 26 | 27 | if not req or req["app_id"] < 1 : 28 | raise Exception(_("Please select the application you want to develop.")) 29 | 30 | if len(globalContext) < 4 : 31 | Requirement.update_requirement(requirement_id=requirementID, tenant_id=tenantID, requirement_name=userPrompt, status=REQUIREMENT_STATUS_InProgress) 32 | 33 | # 开始创建一个需求账单 34 | if GRADE != "base": 35 | TenantBill.record_requirement(tenantID, userName, requirementID, userPrompt) 36 | 37 | appArchitecture, _ = getAppArchitecture(req["app_id"]) 38 | msg, success = clarifyRequirement(requirementID, userPrompt, globalContext, appArchitecture, req) 39 | 40 | if success: 41 | return {'message': msg, "input_prompt": userPrompt} 42 | else: 43 | raise Exception(_("Failed to clarify requirement.")) 44 | -------------------------------------------------------------------------------- /backend/app/controllers/step_subtask.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | from app.pkgs.tools import storage 3 | from app.controllers.common import json_response 4 | from app.pkgs.devops.local_tools import getFileContent 5 | from app.pkgs.tools.i18b import getI18n 6 | from flask import Blueprint 7 | from app.pkgs.prompt.prompt import splitTask, splitTaskDo 8 | from app.pkgs.knowledge.app_info import getServiceBasePrompt, getServiceInfo, getServiceIntro, getServiceLib, getServiceStruct 9 | from app.models.requirement import Requirement 10 | from app.models.application_service import ApplicationService 11 | from app.pkgs.tools.file_tool import get_base_path, get_ws_path 12 | 13 | bp = Blueprint('step_subtask', __name__, url_prefix='/step_subtask') 14 | 15 | @bp.route('/analysis', methods=['POST']) 16 | @json_response 17 | def analysis(): 18 | _ = getI18n("controllers") 19 | data = request.json 20 | serviceName = data['service_name'] 21 | prompt = data['prompt'] 22 | doc_type = data['doc_type'] 23 | username = storage.get("username") 24 | requirementID = request.json.get('task_id') 25 | tenantID = storage.get("tenant_id") 26 | 27 | req = Requirement.get_requirement_by_id(requirementID, tenantID) 28 | 29 | if doc_type == "api": 30 | requirementDoc = req["original_requirement"] 31 | newfeature = requirementDoc+""" 32 | 33 | You need to think on the basis of the following interface documentation: 34 | ``` 35 | """+prompt.replace("```", "")+""" 36 | ``` 37 | """ 38 | else: 39 | requirementDoc = prompt 40 | Requirement.update_requirement(requirement_id=requirementID, tenant_id=tenantID, original_requirement=prompt) 41 | newfeature = requirementDoc 42 | 43 | appBasePrompt, _ = getServiceBasePrompt(req["app_id"], serviceName) 44 | projectIntro, _ = getServiceIntro(req["app_id"], serviceName, tenantID) 45 | projectLib, _ = getServiceLib(req["app_id"], serviceName) 46 | serviceStruct, _ = getServiceStruct(req["app_id"], serviceName) 47 | projectInfo, _ = getServiceInfo(req["app_id"], serviceName, tenantID) 48 | 49 | subtask, success = splitTask(projectInfo, requirementID, newfeature, serviceName, appBasePrompt, projectIntro, projectLib, serviceStruct, req["app_id"], tenantID) 50 | 51 | if success and subtask: 52 | return {'message': subtask, 'service_name': serviceName} 53 | else: 54 | raise Exception(_("Failed to split task.")) 55 | 56 | @bp.route('/task_split', methods=['POST']) 57 | @json_response 58 | def task_split(): 59 | _ = getI18n("controllers") 60 | data = request.json 61 | service_name = data['service_name'] 62 | tec_doc = data['prompt'] 63 | task_id = request.json.get('task_id') 64 | tenant_id = storage.get("tenant_id") 65 | 66 | req_info = Requirement.get_requirement_by_id(task_id, tenant_id) 67 | service_info = ApplicationService.get_service_by_name(req_info["app_id"], service_name) 68 | 69 | filesToEdit, success = splitTaskDo(req_info, service_info, tec_doc, tenant_id) 70 | 71 | git_path = service_info["git_path"] 72 | bath_path = get_base_path(task_id, git_path) 73 | if success and filesToEdit: 74 | for index, file in enumerate(filesToEdit): 75 | file_path = file["file-path"] if 'file-path' in file else file["file_path"] 76 | isSuccess, oldCode = getFileContent(file_path, bath_path) 77 | filesToEdit[index]["old-code"] = oldCode 78 | if not isSuccess: 79 | filesToEdit[index]["old-code"] = '' 80 | 81 | reference_file = file["reference-file"] if 'reference-file' in file else '' 82 | isSuccess, referenceCode = getFileContent(reference_file, bath_path) 83 | filesToEdit[index]["reference-code"] = referenceCode 84 | if not isSuccess: 85 | filesToEdit[index]["reference-code"] = '' 86 | 87 | plugin = {"name": 'task_list', "info": {"files":filesToEdit, "service_name": service_name}} 88 | 89 | return {'plugin': plugin} 90 | else: 91 | raise Exception(_("Failed to split task.")) 92 | -------------------------------------------------------------------------------- /backend/app/controllers/tenant_pro.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from app.controllers.common import json_response 3 | from app.pkgs.tools.i18b import getI18n 4 | 5 | bp = Blueprint('tenant', __name__, url_prefix='/tenant') 6 | 7 | def test(): 8 | pass 9 | 10 | @bp.route('/get_all', methods=['GET']) 11 | @json_response 12 | def get_all(): 13 | _ = getI18n("controllers") 14 | raise Exception(_("The current version does not support this feature.")) 15 | 16 | @bp.route('/create', methods=['POST']) 17 | @json_response 18 | def create(): 19 | _ = getI18n("controllers") 20 | raise Exception(_("The current version does not support this feature.")) -------------------------------------------------------------------------------- /backend/app/controllers/tencent_pro.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | bp = Blueprint('tencent', __name__, url_prefix='/tencent') 4 | 5 | def test(): 6 | pass -------------------------------------------------------------------------------- /backend/app/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /backend/app/flask_ext.py: -------------------------------------------------------------------------------- 1 | from flask_limiter import Limiter 2 | from flask import request 3 | from flask_limiter.util import get_remote_address 4 | 5 | 6 | def limit_ip_func(): 7 | ip = str(request.headers.get("X-Forwarded-For", '127.0.0.1')) 8 | remote_ip = get_remote_address() 9 | 10 | print("limit ip : ", ip, remote_ip) 11 | 12 | if ip != '127.0.0.1': 13 | return ip 14 | 15 | if remote_ip != '127.0.0.1': 16 | return remote_ip 17 | 18 | return '127.0.0.1' 19 | 20 | 21 | limiter_ip = Limiter(key_func=limit_ip_func) 22 | -------------------------------------------------------------------------------- /backend/app/models/__init__.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | 5 | class User(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | username = db.Column(db.String(50), unique=True) 8 | # 其他字段 -------------------------------------------------------------------------------- /backend/app/models/application.py: -------------------------------------------------------------------------------- 1 | from app.extensions import db 2 | from app.models.application_service import ApplicationService 3 | 4 | class Application(db.Model): 5 | app_id = db.Column(db.Integer, primary_key=True) 6 | tenant_id = db.Column(db.Integer, nullable=False) 7 | git_config = db.Column(db.Integer, nullable=False) 8 | ci_config = db.Column(db.Integer, nullable=False) 9 | cd_config = db.Column(db.Integer, nullable=False) 10 | creater = db.Column(db.String(255), nullable=False) 11 | name = db.Column(db.String(255), nullable=False) 12 | description = db.Column(db.Text) 13 | default_source_branch = db.Column(db.String(255)) 14 | default_target_branch = db.Column(db.String(255)) 15 | 16 | def create(tenant_id, creater, name, description, default_source_branch, default_target_branch, git_config, ci_config, cd_config): 17 | if not tenant_id: 18 | tenant_id = 0 19 | 20 | if len(name) < 2: 21 | return "The name field cannot be empty", False 22 | if len(description) < 2: 23 | return "The description field cannot be empty", False 24 | 25 | app = Application( 26 | tenant_id=tenant_id, 27 | creater=creater, 28 | name=name, 29 | git_config=git_config, 30 | ci_config=ci_config, 31 | cd_config=cd_config, 32 | description=description, 33 | default_source_branch=default_source_branch, 34 | default_target_branch=default_target_branch 35 | ) 36 | db.session.add(app) 37 | db.session.commit() 38 | return app, True 39 | 40 | @staticmethod 41 | def get_all_application(tenant_id, appID): 42 | tenant_id = int(tenant_id) 43 | applications = Application.query.order_by(Application.app_id.desc()).all() 44 | print("6666666") 45 | print(appID) 46 | if appID: 47 | applications = Application.query.order_by(Application.app_id.desc()).filter_by(app_id=appID).all() 48 | print(applications) 49 | else: 50 | applications = Application.query.order_by(Application.app_id.desc()).filter_by(tenant_id=tenant_id).all() 51 | 52 | application_list = [] 53 | 54 | for app in applications: 55 | app_dict = { 56 | 'app_id': app.app_id, 57 | 'tenant_id': app.tenant_id, 58 | 'creater': app.creater, 59 | 'name': app.name, 60 | 'git_config': app.git_config, 61 | 'ci_config': app.ci_config, 62 | 'cd_config': app.cd_config, 63 | 'description': app.description, 64 | 'default_source_branch': app.default_source_branch, 65 | 'default_target_branch': app.default_target_branch, 66 | 'service': ApplicationService.get_services_by_app_id(app.app_id) 67 | } 68 | # 0 为模板,也可以获取 69 | if app_dict["tenant_id"] == 0 or tenant_id == 0 or tenant_id == app_dict["tenant_id"]: 70 | application_list.append(app_dict) 71 | 72 | return application_list 73 | 74 | def get_application_by_id(appID, tenant_id=0): 75 | tenant_id = int(tenant_id) 76 | app = Application.query.get(appID) 77 | app_dict = None 78 | if app: 79 | app_dict = { 80 | 'app_id': app.app_id, 81 | 'tenant_id': app.tenant_id, 82 | 'creater': app.creater, 83 | 'name': app.name, 84 | 'git_config': app.git_config, 85 | 'ci_config': app.ci_config, 86 | 'cd_config': app.cd_config, 87 | 'description': app.description, 88 | 'default_source_branch': app.default_source_branch, 89 | 'default_target_branch': app.default_target_branch, 90 | 'service': ApplicationService.get_services_by_app_id(app.app_id) 91 | } 92 | if tenant_id and tenant_id != app_dict["tenant_id"]: 93 | return None 94 | return app_dict 95 | 96 | def update_application(app_id, tenant_id, **kwargs): 97 | tenant_id = int(tenant_id) 98 | app = Application.query.get(app_id) 99 | 100 | if app: 101 | if tenant_id and tenant_id != app.tenant_id: 102 | return None 103 | 104 | for key, value in kwargs.items(): 105 | setattr(app, key, value) 106 | db.session.commit() 107 | return app 108 | return None 109 | -------------------------------------------------------------------------------- /backend/app/models/application_service_lib.py: -------------------------------------------------------------------------------- 1 | from app.extensions import db 2 | 3 | class ApplicationServiceLib(db.Model): 4 | lib_id = db.Column(db.Integer, primary_key=True) 5 | service_id = db.Column(db.Integer, db.ForeignKey('application_service.service_id'), nullable=False) 6 | sys_lib_name = db.Column(db.String(200)) 7 | created_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp()) 8 | updated_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp(), onupdate=db.func.current_timestamp()) 9 | 10 | def create_libs(service_id, libs_name): 11 | import re 12 | separator_pattern = r'[,,]' 13 | libs_array = re.split(separator_pattern, libs_name) 14 | 15 | # 遍历数组中的元素 16 | libs = [] 17 | for sys_lib_name in libs_array: 18 | libs.append(ApplicationServiceLib.create_lib(service_id, sys_lib_name)) 19 | return libs 20 | 21 | # 创建Lib 22 | def create_lib(service_id, sys_lib_name): 23 | lib = ApplicationServiceLib( 24 | service_id=service_id, 25 | sys_lib_name=sys_lib_name 26 | ) 27 | db.session.add(lib) 28 | db.session.commit() 29 | return lib 30 | 31 | # 查询所有Lib 32 | def get_all_libs(): 33 | return ApplicationServiceLib.query.all() 34 | 35 | # 根据lib_id查询Lib 36 | def get_lib_by_id(lib_id): 37 | return ApplicationServiceLib.query.get(lib_id) 38 | 39 | # 更新Lib信息 40 | def update_lib(lib_id, sys_lib_name): 41 | lib = ApplicationServiceLib.query.get(lib_id) 42 | if lib: 43 | lib.sys_lib_name = sys_lib_name 44 | db.session.commit() 45 | return lib 46 | return None 47 | 48 | # 删除Lib 49 | def delete_lib(lib_id): 50 | lib = ApplicationServiceLib.query.get(lib_id) 51 | if lib: 52 | db.session.delete(lib) 53 | db.session.commit() 54 | return True 55 | return False 56 | 57 | def get_libs_by_service_id(service_id): 58 | libs = ApplicationServiceLib.query.filter_by(service_id=service_id).all() 59 | libs_list = [] 60 | 61 | for lib in libs: 62 | lib_dict = { 63 | 'lib_id': lib.lib_id, 64 | 'service_id': lib.service_id, 65 | 'sys_lib_name': lib.sys_lib_name 66 | } 67 | libs_list.append(lib_dict) 68 | 69 | return libs_list 70 | -------------------------------------------------------------------------------- /backend/app/models/async_task_record.py: -------------------------------------------------------------------------------- 1 | from app.extensions import db 2 | 3 | 4 | class AsyncTaskRecord(db.Model): 5 | id = db.Column(db.Integer, primary_key=True) 6 | task_id = db.Column(db.Integer, nullable=False) 7 | step_idx = db.Column(db.Integer, nullable=False) 8 | 9 | task_record_title = db.Column(db.String(200)) 10 | task_record_content = db.Column(db.String(200)) 11 | 12 | created_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp()) 13 | updated_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp(), onupdate=db.func.current_timestamp()) 14 | 15 | @staticmethod 16 | def create_record(task_id, step_idx, task_record_title, task_record_content): 17 | st = AsyncTaskRecord(task_id=task_id, step_idx=step_idx, task_record_title=task_record_title, 18 | task_record_content=task_record_content) 19 | db.session.add(st) 20 | db.session.commit() 21 | return st 22 | 23 | @staticmethod 24 | def get_record_by_task_id_and_step(task_id, step_idx): 25 | records = AsyncTaskRecord.query.filter_by(task_id=task_id, step_idx=step_idx).order_by(AsyncTaskRecord.id.desc()).limit(1).all() 26 | if len(records) == 1: 27 | return records[0] 28 | else: 29 | return None 30 | -------------------------------------------------------------------------------- /backend/app/models/repo_pro.py: -------------------------------------------------------------------------------- 1 | from app.extensions import db 2 | 3 | class Repo(db.Model): 4 | __tablename__ = 'repo' 5 | 6 | # The current version does not support this feature 7 | -------------------------------------------------------------------------------- /backend/app/models/requirement_memory_pro.py: -------------------------------------------------------------------------------- 1 | class RequirementMemory(): 2 | def get_all_requirement_memories(x=None, y=1): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/setting.py: -------------------------------------------------------------------------------- 1 | from app.models.setting_basic import SettingBasic 2 | from app.models.setting_pro import SettingPro 3 | from config import GRADE 4 | 5 | 6 | def getGitConfigList(tenantID, appID, hideToken=True): 7 | if GRADE == "base": 8 | obj = SettingBasic() 9 | else: 10 | obj = SettingPro() 11 | 12 | return obj.getGitConfigList(tenantID, appID, hideToken=hideToken) 13 | 14 | def getCIConfigList(tenantID, appID, hideToken=True): 15 | if GRADE == "base": 16 | obj = SettingBasic() 17 | else: 18 | obj = SettingPro() 19 | 20 | return obj.getCIConfigList(tenantID, appID, hideToken=hideToken) 21 | 22 | def getCDConfigList(tenantID, appID, hideToken=True): 23 | if GRADE == "base": 24 | obj = SettingBasic() 25 | else: 26 | obj = SettingPro() 27 | 28 | return obj.getCDConfigList(tenantID, appID, hideToken=hideToken) 29 | 30 | def getLLMConfigList(tenantID, appID): 31 | if GRADE == "base": 32 | obj = SettingBasic() 33 | else: 34 | obj = SettingPro() 35 | 36 | return obj.getLLMConfigList(tenantID, appID) -------------------------------------------------------------------------------- /backend/app/models/setting_basic.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.tools import storage 2 | from app.models.setting_interface import SettingInterface 3 | from app.pkgs.tools.utils_tool import hide_half_str 4 | from config import DEVOPS_TOOLS, GIT_URL, GIT_TOKEN, GIT_USERNAME, GIT_EMAIL, GIT_API, CD_TOOLS, CD_ACCESS_KEY, CD_SECRET_KEY, GPT_KEYS 5 | 6 | class SettingBasic(SettingInterface): 7 | def getGitConfigList(self, tenantID, appID, hideToken = False): 8 | gitList = [] 9 | name = "Public git config" 10 | if storage.get("language") == 'zh': 11 | name = "公共Git配置" 12 | public_cfg = { 13 | "name" : name, 14 | "git_provider" : DEVOPS_TOOLS, 15 | "git_url" : GIT_URL, 16 | "git_token" : GIT_TOKEN, 17 | "git_config_id" : 0, 18 | "git_username" : GIT_USERNAME, 19 | "git_email" : GIT_EMAIL 20 | } 21 | if hideToken: 22 | public_cfg["git_token"] = hide_half_str(public_cfg["git_token"]) 23 | 24 | gitList.append(public_cfg) 25 | 26 | return gitList, True 27 | 28 | def getCIConfigList(self, tenantID, appID, hideToken=False): 29 | gitList = [] 30 | name = "Public CI config" 31 | if storage.get("language") == 'zh': 32 | name = "公共CI配置" 33 | public_cfg = { 34 | "name" : name, 35 | "ci_provider" : DEVOPS_TOOLS, 36 | "ci_config_id" : 0, 37 | "ci_api_url" : GIT_API, 38 | "git_url" : GIT_URL, 39 | "ci_token" : GIT_TOKEN 40 | } 41 | if hideToken: 42 | public_cfg["ci_token"] = hide_half_str(public_cfg["ci_token"]) 43 | 44 | gitList.append(public_cfg) 45 | 46 | return gitList, True 47 | 48 | def getCDConfigList(self, tenantID, appID, hideToken): 49 | gitList = [] 50 | name = "Public CD config" 51 | if storage.get("language") == 'zh': 52 | name = "公共CD配置" 53 | public_cfg = { 54 | "name" : name, 55 | "cd_provider" : CD_TOOLS, 56 | "ACCESS_KEY" : CD_ACCESS_KEY, 57 | "SECRET_KEY" : CD_SECRET_KEY, 58 | "cd_config_id" : 0 59 | } 60 | if hideToken: 61 | public_cfg["ACCESS_KEY"] = hide_half_str(public_cfg["ACCESS_KEY"]) 62 | public_cfg["SECRET_KEY"] = hide_half_str(public_cfg["SECRET_KEY"]) 63 | 64 | gitList.append(public_cfg) 65 | 66 | return gitList, True 67 | 68 | def getLLMConfigList(self, tenantID, appID): 69 | gptKeys = GPT_KEYS 70 | gitList = [] 71 | 72 | for key in gptKeys["openai"]["keys"]: 73 | for keykey in key: 74 | print(keykey) 75 | gitList.append({ 76 | "llm_config_id" : 0, 77 | "llm_provider" : gptKeys["openai"]["api_type"], 78 | "llm_api_url" : gptKeys["openai"]["api_base"], 79 | "llm_api_version" : gptKeys["openai"]["api_version"], 80 | "llm_api_proxy" : gptKeys["openai"]["proxy"], 81 | "llm_key": keykey 82 | }) 83 | 84 | return gitList, True -------------------------------------------------------------------------------- /backend/app/models/setting_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class SettingInterface(ABC): 6 | @abstractmethod 7 | def getGitConfigList(self, owner): 8 | pass 9 | 10 | @abstractmethod 11 | def getCIConfigList(self, owner): 12 | pass 13 | 14 | @abstractmethod 15 | def getCDConfigList(self, owner): 16 | pass -------------------------------------------------------------------------------- /backend/app/models/setting_pro.py: -------------------------------------------------------------------------------- 1 | from app.models.setting_interface import SettingInterface 2 | 3 | class SettingPro(SettingInterface): 4 | def getGitConfigList(self, tenantID, appID): 5 | # The current version does not support this feature 6 | return "", False 7 | 8 | def getCIConfigList(self, tenantID, appID): 9 | # The current version does not support this feature 10 | return "", False 11 | 12 | def getCDConfigList(self, tenantID, appID): 13 | # The current version does not support this feature 14 | return "", False 15 | 16 | def getLLMConfigList(self, tenantID, appID): 17 | # The current version does not support this feature 18 | return "", False -------------------------------------------------------------------------------- /backend/app/models/sys_lib.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import func 2 | from app.extensions import db 3 | 4 | class SysLib(db.Model): 5 | sys_lib_id = db.Column(db.Integer, primary_key=True) 6 | lib_name = db.Column(db.String(255)) 7 | purpose = db.Column(db.String(255)) 8 | specification = db.Column(db.String(255)) 9 | created_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp()) 10 | updated_at = db.Column(db.TIMESTAMP, default=db.func.current_timestamp(), onupdate=db.func.current_timestamp()) 11 | 12 | @staticmethod 13 | def create_lib(lib_name, purpose, specification): 14 | sys_lib = SysLib( 15 | lib_name=lib_name, 16 | purpose=purpose, 17 | specification=specification 18 | ) 19 | db.session.add(sys_lib) 20 | db.session.commit() 21 | return sys_lib 22 | 23 | @staticmethod 24 | def get_all_libs(): 25 | libs = SysLib.query.all() 26 | lib_list = [] 27 | 28 | for lib in libs: 29 | lib_dict = { 30 | 'sys_lib_id': lib.sys_lib_id, 31 | 'lib_name': lib.lib_name, 32 | 'purpose': lib.purpose, 33 | 'specification': lib.specification, 34 | 'created_at': lib.created_at, 35 | 'updated_at': lib.updated_at 36 | } 37 | lib_list.append(lib_dict) 38 | 39 | return lib_list 40 | 41 | @staticmethod 42 | def get_lib_by_name(sys_lib_name): 43 | sys_lib_name = sys_lib_name.lower() 44 | 45 | libs = SysLib.query.filter(func.lower(SysLib.lib_name).ilike(sys_lib_name)).all() 46 | 47 | lib_dict = {} 48 | for lib in libs: 49 | lib_dict = { 50 | 'sys_lib_id': lib.sys_lib_id, 51 | 'lib_name': lib.lib_name, 52 | 'purpose': lib.purpose, 53 | 'specification': lib.specification, 54 | 'created_at': lib.created_at, 55 | 'updated_at': lib.updated_at 56 | } 57 | 58 | return lib_dict 59 | 60 | @staticmethod 61 | def update_lib(sys_lib_id, lib_name, purpose, specification): 62 | sys_lib = SysLib.query.get(sys_lib_id) 63 | if sys_lib: 64 | sys_lib.lib_name = lib_name 65 | sys_lib.purpose = purpose 66 | sys_lib.specification = specification 67 | db.session.commit() 68 | return sys_lib 69 | return None 70 | 71 | @staticmethod 72 | def delete_lib(sys_lib_id): 73 | sys_lib = SysLib.query.get(sys_lib_id) 74 | if sys_lib: 75 | db.session.delete(sys_lib) 76 | db.session.commit() 77 | return True 78 | return False 79 | -------------------------------------------------------------------------------- /backend/app/models/tenant_bill_pro.py: -------------------------------------------------------------------------------- 1 | class TenantBill(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/tenant_cd_config_pro.py: -------------------------------------------------------------------------------- 1 | class TenantCDConfig(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/tenant_ci_config_pro.py: -------------------------------------------------------------------------------- 1 | class TenantCIConfig(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/tenant_git_config_pro.py: -------------------------------------------------------------------------------- 1 | class TenantGitConfig(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/tenant_pro.py: -------------------------------------------------------------------------------- 1 | class Tenant(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/tenant_user_pro.py: -------------------------------------------------------------------------------- 1 | class TenantUser(): 2 | def test(): 3 | pass -------------------------------------------------------------------------------- /backend/app/models/user.py: -------------------------------------------------------------------------------- 1 | from config import USERS 2 | 3 | class User(): 4 | def checkPassword(username, password): 5 | if username in USERS and USERS[username] == password: 6 | return True 7 | else: 8 | return False -------------------------------------------------------------------------------- /backend/app/models/user_pro.py: -------------------------------------------------------------------------------- 1 | class UserPro(): 2 | __tablename__ = 'user' 3 | 4 | def checkPassword(username, password): 5 | # The current version does not support this feature 6 | return True 7 | 8 | def gen_launch_code(phone, code_type): 9 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app/pkgs/__init__.py -------------------------------------------------------------------------------- /backend/app/pkgs/analyzer_code_exception.py: -------------------------------------------------------------------------------- 1 | class AnalyzerCodeException(Exception): 2 | def __init__(self, message, error_code): 3 | super().__init__(message) 4 | self.message = message 5 | self.error_code = error_code 6 | 7 | 8 | class AnalyzerCodeProcessException(Exception): 9 | def __init__(self, message, error_code, task_no, repo): 10 | super().__init__(message) 11 | self.message = message 12 | self.error_code = error_code 13 | self.task_no = task_no 14 | self.repo = repo 15 | -------------------------------------------------------------------------------- /backend/app/pkgs/devops/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app/pkgs/devops/__init__.py -------------------------------------------------------------------------------- /backend/app/pkgs/devops/cd.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.devops.cd_aliyun import CDAliyun 2 | from app.pkgs.devops.cd_local import CDLocal 3 | from app.pkgs.devops.devops_pro import triggerCDPro 4 | from app.pkgs.devops.cd_awsecs import CDAWS 5 | from config import GRADE 6 | 7 | def triggerCD(requirementID, image, serviceInfo, cdConfig): 8 | CD_TOOLS = cdConfig["cd_provider"] 9 | 10 | if CD_TOOLS == 'local': 11 | obj = CDLocal() 12 | elif CD_TOOLS == 'aliyun': 13 | obj = CDAliyun() 14 | elif CD_TOOLS == 'aws': 15 | obj = CDAWS() 16 | 17 | re, success = obj.triggerCD(image, serviceInfo, cdConfig) 18 | 19 | if GRADE != "base": 20 | triggerCDPro(requirementID, image, re, cdConfig) 21 | 22 | return re, success -------------------------------------------------------------------------------- /backend/app/pkgs/devops/cd_aliyun.py: -------------------------------------------------------------------------------- 1 | from flask import json 2 | from app.pkgs.devops.cd_interface import CDInterface 3 | from aliyunsdkcore.client import AcsClient 4 | from aliyunsdkeci.request.v20180808.DescribeContainerGroupsRequest import DescribeContainerGroupsRequest 5 | from aliyunsdkeci.request.v20180808 import CreateContainerGroupRequest 6 | from aliyunsdkeci.request.v20180808 import DeleteContainerGroupRequest 7 | import time 8 | 9 | # https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-eci/aliyunsdkeci/request/v20180808/DeleteContainerGroupRequest.py 10 | # https://help.aliyun.com/zh/eci/developer-reference/api-eci-2018-08-08-createcontainergroup 11 | class CDAliyun(CDInterface): 12 | def triggerCD(self, image, serviceInfo, cdConfig): 13 | self.client = AcsClient(cdConfig["ACCESS_KEY"], cdConfig["SECRET_KEY"], serviceInfo["cd_region"]) 14 | # 创建ECI容器组 15 | request = CreateContainerGroupRequest.CreateContainerGroupRequest() 16 | request.set_ContainerGroupName(serviceInfo["cd_container_group"]) 17 | request.set_RestartPolicy('Never') 18 | request.set_AutoCreateEip(True) 19 | request.set_EipBandwidth(1) 20 | request.set_SecurityGroupId(serviceInfo["cd_security_group"]) 21 | request.set_VSwitchId(serviceInfo["cd_subnet"]) 22 | 23 | container_definition = { 24 | "Name": serviceInfo["cd_container_name"], 25 | "Image": image, 26 | "Cpu": 0.5, 27 | "Memory": 1, 28 | 'ImagePullPolicy': "Always" 29 | } 30 | 31 | request.set_Containers([container_definition]) 32 | 33 | response = self.client.do_action_with_exception(request) 34 | response_json = json.loads(response) 35 | container_group_id = response_json["ContainerGroupId"] 36 | print(container_group_id) 37 | print(response) 38 | 39 | # 等待容器组创建完成 40 | times = 0 41 | while True: 42 | if times >= 30: 43 | del_request = DeleteContainerGroupRequest.DeleteContainerGroupRequest() 44 | del_request.set_ContainerGroupId(container_group_id) 45 | self.client.do_action_with_exception(del_request) 46 | return f'部署失败。Deployment failure', True 47 | times = times + 1 48 | try: 49 | time.sleep(10) 50 | request = DescribeContainerGroupsRequest() 51 | request.set_ContainerGroupIds([container_group_id]) 52 | eci_info = self.client.do_action_with_exception(request) 53 | print(eci_info) 54 | eci_info_json = json.loads(eci_info) 55 | status = eci_info_json['ContainerGroups'][0]['Status'] 56 | if status == 'Running': 57 | break 58 | except Exception as e: 59 | print(str(e)) 60 | break 61 | 62 | # 获取容器组的公网IP地址 63 | request = DescribeContainerGroupsRequest() 64 | request.set_ContainerGroupIds([container_group_id]) 65 | eci_info = self.client.do_action_with_exception(request) 66 | eci_info_json = json.loads(eci_info) 67 | print(eci_info) 68 | public_ip = eci_info_json['ContainerGroups'][0]['InternetIp'] 69 | 70 | return f'访问网址:http://{public_ip} (本环境仅供体验,1小时候将自动删除。This environment is for experience only and will be deleted after 1 hour)', True 71 | -------------------------------------------------------------------------------- /backend/app/pkgs/devops/cd_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | # todo finish CD and cloud services 4 | class CDInterface(ABC): 5 | @abstractmethod 6 | def triggerCD(self, image, container_grpup, container_name, cdConfigList): 7 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/devops/cd_local.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.devops.cd_interface import CDInterface 2 | from config import WORKSPACE_PATH 3 | 4 | class CDLocal(CDInterface): 5 | def triggerCD(self, image, serviceInfo, cdConfig): 6 | return "The pipeline cannot be run locally now, you can view the configuration and select tools such as gitlab.", False -------------------------------------------------------------------------------- /backend/app/pkgs/devops/devops.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.devops.devops_gitlab import DevopsGitlab 2 | from app.pkgs.devops.devops_local import DevopsLocal 3 | from app.pkgs.devops.devops_github import DevopsGitHub 4 | from app.pkgs.devops.devops_pro import triggerPipelinePro 5 | from config import GRADE 6 | 7 | def triggerPipeline(requirementID, branchName, serviceInfo, ciConfig): 8 | DEVOPS_TOOLS = ciConfig["ci_provider"] 9 | 10 | if DEVOPS_TOOLS == 'local': 11 | obj = DevopsLocal() 12 | elif DEVOPS_TOOLS == 'gitlab' or DEVOPS_TOOLS == 'GitLab': 13 | obj = DevopsGitlab() 14 | elif DEVOPS_TOOLS == 'github' or DEVOPS_TOOLS == 'GitHub': 15 | obj = DevopsGitHub() 16 | 17 | result, piplineID, piplineUrl, success = obj.triggerPipeline(branchName, serviceInfo, ciConfig) 18 | 19 | if GRADE != "base": 20 | triggerPipelinePro(requirementID, branchName, {"piplineUrl": piplineUrl, "piplineID": piplineID, "repopath": serviceInfo["git_path"]}, ciConfig) 21 | 22 | return result, piplineID, piplineUrl, success 23 | 24 | def getPipelineStatus(piplineId, repoPath, ciConfig): 25 | DEVOPS_TOOLS = ciConfig["ci_provider"] 26 | 27 | if DEVOPS_TOOLS == 'local': 28 | obj = DevopsLocal() 29 | elif DEVOPS_TOOLS == 'gitlab' or DEVOPS_TOOLS == 'GitLab': 30 | obj = DevopsGitlab() 31 | elif DEVOPS_TOOLS == 'github' or DEVOPS_TOOLS == 'GitHub': 32 | obj = DevopsGitHub() 33 | 34 | return obj.getPipelineStatus(piplineId, repoPath, ciConfig) 35 | 36 | def getPipelineJobLogs(repopath, pipeline_id, job_id, ciConfig): 37 | DEVOPS_TOOLS = ciConfig["ci_provider"] 38 | 39 | if DEVOPS_TOOLS == 'local': 40 | obj = DevopsLocal() 41 | elif DEVOPS_TOOLS == 'gitlab' or DEVOPS_TOOLS == 'GitLab': 42 | obj = DevopsGitlab() 43 | elif DEVOPS_TOOLS == 'github' or DEVOPS_TOOLS == 'GitHub': 44 | obj = DevopsGitHub() 45 | 46 | return obj.getPipelineJobLogs(obj, repopath, pipeline_id, job_id, ciConfig) -------------------------------------------------------------------------------- /backend/app/pkgs/devops/devops_gitlab.py: -------------------------------------------------------------------------------- 1 | import gitlab 2 | import html 3 | import re 4 | from app.pkgs.devops.devops_interface import DevopsInterface 5 | 6 | class DevopsGitlab(DevopsInterface): 7 | def triggerPipeline(self, branch_name, serviceInfo, ciConfig): 8 | repopath = serviceInfo["git_path"] 9 | apiUrl = ciConfig["ci_api_url"] 10 | try: 11 | gl = gitlab.Gitlab(apiUrl, ciConfig["git_token"], api_version='4') 12 | 13 | project = gl.projects.get(repopath) 14 | pipeline = project.pipelines.create({'ref': branch_name}) 15 | pipeline_url = apiUrl + '/' + repopath + '/-/pipelines/' + str(pipeline.id) 16 | return "Get pipline status...", str(pipeline.get_id()), pipeline_url, True 17 | except Exception as e: 18 | return f"Failed to trigger pipline giturl:{apiUrl} repopath:{repopath} branch:{branch_name}, Error:" + str(e), 0, "", False 19 | 20 | 21 | def getPipelineStatus(self, pipline_id, repopath, ciConfig): 22 | apiURL = ciConfig["ci_api_url"] 23 | try: 24 | gl = gitlab.Gitlab(apiURL, ciConfig["ci_token"], api_version='4') 25 | 26 | project = gl.projects.get(repopath) 27 | 28 | pipeline = project.pipelines.get(pipline_id) 29 | 30 | print("pipeline:", pipeline.status) 31 | 32 | jobs = pipeline.jobs.list() 33 | 34 | job_info = [] 35 | docker_image = "" 36 | for job in jobs: 37 | print("job:", job) 38 | job_log = self.getPipelineJobLogs(repopath, pipline_id, job.id) 39 | job_info.append({ 40 | 'job_id': job.id, 41 | 'job_name': job.name, 42 | 'status': job.status, 43 | 'duration': job.duration, 44 | 'log': job_log 45 | }) 46 | img = parseDockerImage(job_log) 47 | if len(img) > 1: 48 | docker_image = img 49 | 50 | return list(reversed(job_info)), docker_image, True 51 | except Exception as e: 52 | return "Failed to get pipline status:" + str(e), '', False 53 | 54 | def getPipelineJobLogs(self, repopath, pipeline_id, job_id, ciConfig): 55 | try: 56 | gl = gitlab.Gitlab(ciConfig["ci_api_url"], ciConfig["ci_token"], api_version='4') 57 | 58 | project = gl.projects.get(repopath) 59 | job = project.jobs.get(job_id) 60 | logs = job.trace() 61 | 62 | return removeColorCodes(logs) 63 | except Exception as e: 64 | return "Failed to get log: " + str(e) 65 | 66 | 67 | def removeColorCodes(log_string): 68 | unicode_string = log_string.decode('unicode_escape') 69 | color_regex = re.compile(r'\x1b\[[0-9;]*m') 70 | cleaned_string = re.sub(color_regex, '', unicode_string) 71 | cleaned_string = re.sub(r'\n', '
', unicode_string) 72 | cleaned_string = re.sub(r'\r', '
', cleaned_string) 73 | cleaned_string = re.sub('"', ' ', cleaned_string) 74 | cleaned_string = re.sub("'", ' ', cleaned_string) 75 | cleaned_string = re.sub( 76 | r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]', ' ', cleaned_string) 77 | cleaned_string = html.escape(cleaned_string) 78 | return cleaned_string 79 | 80 | # def getFileContent(self, file_path, branch_name, repopath): 81 | # try: 82 | # print("get_file_content-repopath:" + repopath) 83 | # print("get_file_content-branch_name:" + branch_name) 84 | # print("get_file_content-file_path:" + file_path) 85 | # gl = gitlab.Gitlab(GIT_URL, private_token=GIT_TOKEN, api_version='4') 86 | 87 | # project = gl.projects.get(repopath) 88 | 89 | # file = project.files.get(file_path, ref=branch_name) 90 | 91 | # content = codecs.decode(file.decode(), 'utf-8') 92 | 93 | # print(content) 94 | 95 | # return True, content 96 | # except Exception as e: 97 | # return False, str(e) 98 | 99 | def parseDockerImage(input_str): 100 | # 定义正则表达式模式 101 | pattern = r'kuafuai_docker_image_pushed:(.+?)[&|\n]' 102 | 103 | # 使用 re.search 来查找匹配项 104 | match = re.search(pattern, input_str) 105 | 106 | # 如果找到匹配项,则提取结果 107 | if match: 108 | result = match.group(1) 109 | return result 110 | else: 111 | print("parseDockerImage: 未找到匹配项") 112 | return "" -------------------------------------------------------------------------------- /backend/app/pkgs/devops/devops_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class DevopsInterface(ABC): 6 | @abstractmethod 7 | def triggerPipeline(self, branchName, repoPath, ciConfig): 8 | pass 9 | 10 | @abstractmethod 11 | def getPipelineStatus(self, pipelineId, repoPath, ciConfig): 12 | pass 13 | 14 | @abstractmethod 15 | def getPipelineJobLogs(self, repopath, pipeline_id, job_id, ciConfig): 16 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/devops/devops_local.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.devops.devops_interface import DevopsInterface 2 | from config import WORKSPACE_PATH 3 | 4 | class DevopsLocal(DevopsInterface): 5 | def triggerPipeline(self, branch_name, serviceInfo, ciConfigList): 6 | return "The pipeline cannot be run locally now, you can view the configuration and select tools such as gitlab.", 0, "", False 7 | 8 | def getPipelineStatus(self, pipline_id, repopath, ciConfigList): 9 | return "The pipeline cannot be run locally now, you can view the configuration and select tools such as gitlab.", 0, "", False 10 | 11 | def getPipelineJobLogs(self, repopath, pipeline_id, job_id, ciConfigList): 12 | return "The pipeline cannot be run locally now, you can view the configuration and select tools such as gitlab.", 0, "", False -------------------------------------------------------------------------------- /backend/app/pkgs/devops/devops_pro.py: -------------------------------------------------------------------------------- 1 | def triggerPipelinePro(requirementID, branchName, re): 2 | pass 3 | 4 | def triggerCDPro(requirementID, image, re): 5 | pass 6 | -------------------------------------------------------------------------------- /backend/app/pkgs/devops/local_tools.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.tools.file_tool import read_file_content 2 | from app.pkgs.devops.local_tools_base import LocalToolsBase 3 | from app.pkgs.devops.local_tools_pro import LocalToolsPro 4 | from config import GRADE 5 | from config import WORKSPACE_PATH 6 | 7 | def getFileContent(file_path, bath_path): 8 | if len(bath_path) > 2: 9 | path = bath_path + "/" + file_path 10 | else: 11 | path = file_path 12 | 13 | try: 14 | success, content = read_file_content(path) 15 | if not success: 16 | return False, "" 17 | except Exception as e: 18 | return False, "" 19 | 20 | return True, content 21 | 22 | def compileCheck(requirementID, ws_path,repo_path): 23 | if GRADE == "base": 24 | obj = LocalToolsBase() 25 | else: 26 | obj = LocalToolsPro() 27 | 28 | return obj.compileCheck(requirementID, ws_path,repo_path) 29 | 30 | def lintCheck(requirementID, ws_path, repo_path, file_path): 31 | if GRADE == "base": 32 | obj = LocalToolsBase() 33 | else: 34 | obj = LocalToolsPro() 35 | 36 | return obj.lintCheck(requirementID, ws_path, repo_path, file_path) 37 | 38 | def unitTest(requirementID, ws_path, repo_path, file_path): 39 | if GRADE == "base": 40 | obj = LocalToolsBase() 41 | else: 42 | obj = LocalToolsPro() 43 | 44 | return obj.unitTest(requirementID, ws_path, repo_path, file_path) 45 | 46 | def apiTest(requirementID, ws_path, repo_path, file_path): 47 | if GRADE == "base": 48 | obj = LocalToolsBase() 49 | else: 50 | obj = LocalToolsPro() 51 | 52 | return obj.apiTest(requirementID, ws_path, repo_path, file_path) -------------------------------------------------------------------------------- /backend/app/pkgs/devops/local_tools_base.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import platform 3 | from app.pkgs.tools.utils_tool import detect_programming_language, get_last_n_lines 4 | from app.pkgs.devops.local_tools_interface import LocalToolsInterface 5 | from config import WORKSPACE_PATH 6 | 7 | class LocalToolsBase(LocalToolsInterface): 8 | def compileCheck(self, requirementID, ws_path, repo_path): 9 | print("compile_check:") 10 | gitCwd = ws_path+'/'+repo_path 11 | script = "" 12 | print(gitCwd) 13 | 14 | if platform.system() == 'Windows': 15 | script = "build.cmd" 16 | sub = [script] 17 | result = subprocess.run( 18 | sub, capture_output=True, text=True, shell=True, cwd=gitCwd) 19 | else: 20 | script = "build.sh" 21 | sub = ['sh', script] 22 | result = subprocess.run( 23 | sub, capture_output=True, text=True, cwd=gitCwd) 24 | 25 | print(result) 26 | if result.returncode != 0: 27 | stderr = get_last_n_lines(result.stderr, 20) 28 | if len(stderr)<5: 29 | stderr = get_last_n_lines(result.stdout, 20) 30 | success = False 31 | re = stderr 32 | else: 33 | success = True 34 | re = result.stdout 35 | 36 | return success, re 37 | 38 | def lintCheck(self, requirementID, ws_path, repo_path, file_path): 39 | if detect_programming_language(file_path) == "Python": 40 | result = subprocess.run( 41 | ['pylint', '--disable=all', '--enable=syntax-error', f"{repo_path}/{file_path}"], capture_output=True, text=True, cwd=ws_path) 42 | else: 43 | return True, "Code Scan PAAS." 44 | 45 | print("lint_check:") 46 | print(ws_path) 47 | print(result) 48 | if result.returncode != 0: 49 | stderr = get_last_n_lines(result.stderr, 20) 50 | if len(stderr)<5: 51 | stderr = get_last_n_lines(result.stdout, 20) 52 | success = False 53 | re = stderr 54 | else: 55 | success = True 56 | re = result.stdout 57 | 58 | return success, re 59 | 60 | def unitTest(self, requirementID, ws_path, repo_path, file_path): 61 | return True, "The current version does not support this feature" 62 | 63 | def apiTest(self, requirementID, ws_path, repo_path, file_path): 64 | return True, "The current version does not support this feature" -------------------------------------------------------------------------------- /backend/app/pkgs/devops/local_tools_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class LocalToolsInterface(ABC): 6 | @abstractmethod 7 | def compileCheck(requirementID, ws_path,repo_path): 8 | pass 9 | 10 | @abstractmethod 11 | def lintCheck(requirementID, ws_path, repo_path, file_path): 12 | pass 13 | 14 | @abstractmethod 15 | def unitTest(requirementID, ws_path, repo_path, file_path): 16 | pass 17 | 18 | @abstractmethod 19 | def apiTest(requirementID, ws_path, repo_path, file_path): 20 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/devops/local_tools_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.devops.local_tools_interface import LocalToolsInterface 2 | 3 | class LocalToolsPro(LocalToolsInterface): 4 | def compileCheck(self, requirementID, ws_path, repo_path): 5 | pass 6 | 7 | def lintCheck(self, requirementID, ws_path, repo_path, file_path): 8 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/knowledge/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app/pkgs/knowledge/__init__.py -------------------------------------------------------------------------------- /backend/app/pkgs/knowledge/app_info_basic.py: -------------------------------------------------------------------------------- 1 | import time 2 | from app.pkgs.knowledge.app_info_interface import AppInfoInterface 3 | from app.pkgs.tools.i18b import getI18n 4 | from app.models.application import Application 5 | from app.models.sys_lib import SysLib 6 | 7 | class AppInfoBasic(AppInfoInterface): 8 | def getServiceLib(self, appID, serviceName): 9 | appID = int(appID) 10 | 11 | appLib = "" 12 | apps = Application.get_all_application(0, appID) 13 | if len(apps) > 0: 14 | services = apps[0]["service"] 15 | for service in services: 16 | if service["name"] == serviceName: 17 | for lib in service["libs"]: 18 | appLib += lib["sys_lib_name"]+"\n" 19 | 20 | return appLib, True 21 | 22 | def getServiceStruct(self, appID, serviceName): 23 | appID = int(appID) 24 | 25 | projectStruct = "" 26 | apps = Application.get_all_application(0, appID) 27 | if len(apps) > 0: 28 | services = apps[0]["service"] 29 | for service in services: 30 | if service["name"] == serviceName: 31 | projectStruct = service["struct_cache"] 32 | 33 | return projectStruct, True 34 | 35 | def getServiceSpecification(self, appID, serviceName, LibName): 36 | appID = int(appID) 37 | 38 | lib = SysLib.get_lib_by_name(LibName) 39 | specification = "" 40 | if "purpose" in lib and "specification" in lib: 41 | specification = lib["purpose"] +", "+ lib["specification"] 42 | 43 | return specification, True 44 | 45 | def analyzeService(self, tenant_id, gitPath): 46 | time.sleep(2) 47 | _ = getI18n("controllers") 48 | reJson = { 49 | "name" : gitPath, 50 | "git_config_id": 0, 51 | "ci_config_id": 0, 52 | "cd_config_id": 0, 53 | "cd_container_name": "", 54 | "cd_container_group": "", 55 | "cd_region": "", 56 | "cd_public_ip": "", 57 | "cd_security_group": "", 58 | "cd_subnet": "", 59 | "git_path": gitPath, 60 | "git_workflow": "default.yaml", 61 | "role": gitPath, 62 | "struct_cache": "unkonwn", 63 | "language": "unkonwn", 64 | "framework": "unkonwn", 65 | "database": "unkonwn", 66 | "api_type": "swagger", 67 | "api_location": "", 68 | "service_libs_name": "no" 69 | } 70 | return reJson, True 71 | 72 | def repo_analyzer(self, type, repo, task_id): 73 | return "", False 74 | -------------------------------------------------------------------------------- /backend/app/pkgs/knowledge/app_info_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | 6 | class AppInfoInterface(ABC): 7 | @abstractmethod 8 | def getServiceLib(self, appID, serviceName): 9 | pass 10 | 11 | @abstractmethod 12 | def getServiceStruct(self, appID, serviceName): 13 | pass 14 | 15 | @abstractmethod 16 | def getServiceSpecification(self, appID, serviceName, LibName): 17 | pass 18 | 19 | @abstractmethod 20 | def analyzeService(self, tenant_id, git_path): 21 | pass 22 | 23 | 24 | def repo_analyzer(self, type, repo, task_id): 25 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/knowledge/app_info_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.knowledge.app_info_interface import AppInfoInterface 2 | 3 | class AppInfoPro(AppInfoInterface): 4 | def getServiceLib(self, appID, serviceName): 5 | # The current version does not support this feature 6 | return "", False 7 | 8 | def getServiceStruct(self, appID, serviceName): 9 | # The current version does not support this feature 10 | return "", False 11 | 12 | def getServiceSpecification(self, appID, serviceName, LibName): 13 | # The current version does not support this feature 14 | return "", False 15 | 16 | def analyzeService(self, gitPath): 17 | # The current version does not support this feature 18 | return "", False -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app/pkgs/prompt/__init__.py -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/api_basic.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.tools.llm import chatCompletion 2 | from app.pkgs.prompt.api_interface import ApiInterface 3 | 4 | class ApiBasic(ApiInterface): 5 | def clarifyAPI(self, requirementID, userPrompt, apiDoc): 6 | message, ctx, success = step1ApiDocTasks(userPrompt, apiDoc) 7 | if success: 8 | return step2GenApiDoc(message, ctx) 9 | else: 10 | return message, False 11 | 12 | def step2GenApiDoc(message, context): 13 | context.append({ 14 | "role": "assistant", 15 | "content": message 16 | }) 17 | 18 | context.append({ 19 | "role": "user", 20 | "content": """Summarize all subtasks to output a swagger API. 21 | 22 | note that it contains only the parts that need to be modified for new requirements, delete the parts that have nothing to do with new requirements. 23 | 24 | Without any dialogue or explanation, just output the final API only."""}) 25 | 26 | message, total_tokens, success = chatCompletion(context, FAKE_API) 27 | return message, success 28 | 29 | def step1ApiDocTasks(user_prompt, apiDoc): 30 | context = [] 31 | content = """As a senior full stack developer, Your task is to design the swagger API and add detailed business logic comments to the API. Now you need to think step-by-step based on the requirements document and the Existing API of the existing project, analyze what needs to be adjusted to meet the requirements, and break down the content into multiple subtasks, describing each subtask in as much detail as possible. 32 | 33 | Attention: 34 | 1. Think repeatedly whether the needs are reasonably satisfied; 35 | 2. Make only minimal changes to meet the requirements; 36 | 3. You are only responsible for meeting the new requirements on the basis of the existing API, and do not modify the parts unrelated to this requirement; 37 | 4. Always reflect, subtasks are only responsible for completing the design requirements of the API, and do not need to focus on the parts unrelated to the API. 38 | 5. try to implement functionality in just one interface. 39 | 40 | requirements document: 41 | ``` 42 | """ + user_prompt + """ 43 | ``` 44 | 45 | Existing API: 46 | ``` 47 | """ + apiDoc + """ 48 | ``` 49 | """ 50 | 51 | context.append({"role": "system", "content": content}) 52 | message, total_tokens, success = chatCompletion(context, FAKE_API) 53 | 54 | return message, context, success 55 | 56 | FAKE_API = """ 57 | Swagger API: 58 | 59 | ``` 60 | paths: 61 | /game/start: 62 | post: 63 | summary: Start the game 64 | responses: 65 | 200: 66 | description: Game started successfully 67 | 400: 68 | description: Game already started 69 | 70 | /game/move: 71 | post: 72 | summary: Move the snake 73 | requestBody: 74 | content: 75 | application/json: 76 | schema: 77 | type: object 78 | properties: 79 | direction: 80 | type: string 81 | enum: [up, down, left, right] 82 | required: 83 | - direction 84 | responses: 85 | 200: 86 | description: Snake moved successfully 87 | 400: 88 | description: Invalid direction provided 89 | 404: 90 | description: Game not found or not started 91 | 92 | /game/state: 93 | get: 94 | summary: Get the current game state 95 | responses: 96 | 200: 97 | description: Returns the current game state 98 | 404: 99 | description: Game not found or not started 100 | 101 | /game/speed: 102 | post: 103 | summary: Adjust the snake's movement speed 104 | requestBody: 105 | content: 106 | application/json: 107 | schema: 108 | type: object 109 | properties: 110 | speed: 111 | type: integer 112 | enum: [1, 2, 3] 113 | required: 114 | - speed 115 | responses: 116 | 200: 117 | description: Snake speed adjusted successfully 118 | 400: 119 | description: Invalid speed provided 120 | 404: 121 | description: Game not found or not started 122 | 123 | /game/end: 124 | post: 125 | summary: End the game 126 | responses: 127 | 200: 128 | description: Game ended successfully 129 | 404: 130 | description: Game not found or not started 131 | ``` 132 | """ -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/api_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class ApiInterface(ABC): 6 | @abstractmethod 7 | def clarifyAPI(self, requirementID, userPrompt, apiDoc): 8 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/api_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.api_interface import ApiInterface 2 | 3 | class ApiPro(ApiInterface): 4 | def clarifyAPI(self, userPrompt, apiDocUrl): 5 | # The current version does not support this feature 6 | return "", False -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/code_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class CodeInterface(ABC): 6 | @abstractmethod 7 | def aiReferenceRepair(self, requirementID, newCode, referenceCode, fileTask, filePath): 8 | pass 9 | 10 | @abstractmethod 11 | def aiAnalyzeError(self, requirementID, message, filePath): 12 | pass 13 | 14 | @abstractmethod 15 | def aiFixError(self, requirementID, solution, code, filePath, type): 16 | pass 17 | 18 | @abstractmethod 19 | def aiCheckCode(self, requirementID, fileTask, code, filePath): 20 | pass 21 | 22 | @abstractmethod 23 | def aiMergeCode(self, requirementID, task, baseCode, newCode, filePath): 24 | pass 25 | 26 | @abstractmethod 27 | def aiGenCode(self, requirementID, fileTask, newTask, newCode, filePath): 28 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/code_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.code_interface import CodeInterface 2 | 3 | class CodePro(CodeInterface): 4 | def aiReferenceRepair(self, newCode, referenceCode, fileTask): 5 | # The current version does not support this feature 6 | return [], False 7 | 8 | def aiAnalyzeError(self, message): 9 | # The current version does not support this feature 10 | return [], False 11 | 12 | def aiFixError(self, solution, code): 13 | # The current version does not support this feature 14 | return [], False 15 | 16 | def aiCheckCode(self, fileTask, code): 17 | # The current version does not support this feature 18 | return [], False 19 | 20 | def aiMergeCode(self, task, baseCode, newCode): 21 | # The current version does not support this feature 22 | return [], False 23 | 24 | def aiGenCode(self, fileTask, newTask, newCode): 25 | # The current version does not support this feature 26 | return [], False 27 | -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/requirement_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class RequirementInterface(ABC): 6 | @abstractmethod 7 | def clarifyRequirement(self, requirementID, userPrompt, globalContext, appArchitecture): 8 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/requirement_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.requirement_interface import RequirementInterface 2 | 3 | 4 | class RequirementPro(RequirementInterface): 5 | def clarifyRequirementPro(self, userPrompt, globalContext, appArchitecture): 6 | # The current version does not support this feature 7 | return [], False 8 | -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/subtask_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class SubtaskInterface(ABC): 6 | @abstractmethod 7 | def splitTask(self, requirementID, feature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, appID): 8 | pass 9 | 10 | @abstractmethod 11 | def splitTaskDo(self, requirementID, feature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, appID): 12 | pass 13 | 14 | @abstractmethod 15 | def write_code(self, requirement_id, service_name, file_path, development_detail, step_id): 16 | pass 17 | -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/subtask_java_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.subtask_interface import SubtaskInterface 2 | 3 | class SubtaskJavaPro(SubtaskInterface): 4 | def splitTask(self, newfeature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, appID): 5 | # The current version does not support this feature 6 | return "", False -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/subtask_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.subtask_interface import SubtaskInterface 2 | 3 | class SubtaskPro(SubtaskInterface): 4 | def splitTask(self, newfeature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, appID): 5 | # The current version does not support this feature 6 | return "", False -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/subtask_python_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.subtask_interface import SubtaskInterface 2 | from app.pkgs.prompt.subtask_pro import SubtaskPro 3 | 4 | class SubtaskPythonPro(SubtaskInterface): 5 | def splitTask(self, requirementID, newfeature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, 6 | appID): 7 | # The current version does not support this feature 8 | return SubtaskPro.splitTask(requirementID, newfeature, serviceName, appBasePrompt, projectInfo, projectLib, 9 | serviceStruct, appID) 10 | 11 | def write_code(self, requirement_id, service_name, file_path, development_detail, step_id): 12 | return SubtaskPro.write_code(requirement_id, service_name, file_path, development_detail, step_id) -------------------------------------------------------------------------------- /backend/app/pkgs/prompt/subtask_vue_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.prompt.subtask_interface import SubtaskInterface 2 | 3 | class SubtaskVuePro(SubtaskInterface): 4 | def splitTask(self, newfeature, serviceName, appBasePrompt, projectInfo, projectLib, serviceStruct, appID): 5 | # The current version does not support this feature 6 | return "", False 7 | -------------------------------------------------------------------------------- /backend/app/pkgs/scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | from .scheduler import task, process_task_time_out 2 | 3 | 4 | def register_job(scheduler, app): 5 | scheduler.add_job(task, 'interval', seconds=5, id="async_task", args=[app], max_instances=5) 6 | scheduler.add_job(process_task_time_out, 'interval', seconds=10, id="async_task_time_out", args=[app]) 7 | -------------------------------------------------------------------------------- /backend/app/pkgs/scheduler/scheduler.py: -------------------------------------------------------------------------------- 1 | from app.models.async_task import AsyncTask 2 | from app.pkgs.knowledge.app_info import repo_analyzer 3 | import json 4 | from datetime import datetime, timedelta 5 | import threading 6 | 7 | def task(app): 8 | print("scanning task ... ", datetime.now(), threading.current_thread().name, flush=True) 9 | with app.app_context(): 10 | # 查询 任务 11 | async_task = AsyncTask.get_analyzer_code_task_one(AsyncTask.Status_Init) 12 | 13 | if async_task: 14 | print("process task token : ", async_task.token, async_task.version, flush=True) 15 | 16 | content = json.loads(async_task.task_content) 17 | type = content['type'] 18 | repo = content['repo'] 19 | lock_task = AsyncTask.update_task_status_and_version(async_task.id, AsyncTask.Status_Running, async_task.version) 20 | if lock_task: 21 | 22 | print("process lock task success token: ", async_task.token, async_task.version, lock_task.version) 23 | 24 | # 查询7天内的第一条成功记录,如果有直接更新结果 25 | history_success_data = AsyncTask.get_analyzer_code_by_name(async_task.task_name) 26 | if history_success_data: 27 | print("find history :", history_success_data.token) 28 | task_status_message = history_success_data.task_status_message 29 | task_name = async_task.task_name+"(history)" 30 | AsyncTask.update_task_status_and_message_and_name(async_task.id, AsyncTask.Status_Done, task_status_message, task_name) 31 | else: 32 | try: 33 | result, success = repo_analyzer(type, repo, async_task.id) 34 | if success: 35 | AsyncTask.update_task_status_and_message(async_task.id, AsyncTask.Status_Done, json.dumps(result)) 36 | else: 37 | AsyncTask.update_task_status_and_message(async_task.id, AsyncTask.Status_Fail, result) 38 | except Exception as e: 39 | AsyncTask.update_task_status_and_message(async_task.id, AsyncTask.Status_Fail, "analyzer error") 40 | 41 | else: 42 | print("process lock task fail token: ", async_task.token) 43 | 44 | 45 | def process_task_time_out(app): 46 | with app.app_context(): 47 | async_task = AsyncTask.get_analyzer_code_task_one(AsyncTask.Status_Running) 48 | if async_task: 49 | current_date_1_hours = datetime.now() - timedelta(minutes=30) 50 | if current_date_1_hours > async_task.created_at: 51 | print("analyzer code timeout:", async_task.token) 52 | AsyncTask.update_task_status_and_message(async_task.id, AsyncTask.Status_Fail, "analyzer process timeout") -------------------------------------------------------------------------------- /backend/app/pkgs/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/backend/app/pkgs/tools/__init__.py -------------------------------------------------------------------------------- /backend/app/pkgs/tools/file_tool.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from config import WORKSPACE_PATH 4 | 5 | 6 | def read_file_content(filename): 7 | print("read_file_content:" + filename) 8 | try: 9 | with open(filename, 'r', encoding='utf-8') as f: 10 | content = f.read() 11 | result = True 12 | except FileNotFoundError: 13 | content = '' 14 | result = False 15 | return result, content 16 | 17 | 18 | def write_file_content(filename, content): 19 | print("write_file_content:" + filename) 20 | directory = os.path.dirname(filename) 21 | os.makedirs(directory, exist_ok=True) 22 | with open(filename, 'w', encoding='utf-8') as f: 23 | f.write(content) 24 | 25 | 26 | def get_ws_path(task_id): 27 | return WORKSPACE_PATH + task_id 28 | 29 | 30 | def get_base_path(task_id, git_path): 31 | ws_path = get_ws_path(task_id) 32 | bath_path = ws_path +"/"+ git_path 33 | return bath_path -------------------------------------------------------------------------------- /backend/app/pkgs/tools/llm.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from app.pkgs.tools.llm_pro import LLMPro 3 | from app.pkgs.tools.llm_basic import LLMBase 4 | from config import GRADE 5 | 6 | def chatCompletion(context, fackData="", bill: bool = True): 7 | if GRADE == "base": 8 | obj = LLMBase() 9 | else: 10 | obj = LLMPro() 11 | 12 | message = "" 13 | success = False 14 | try: 15 | message, total_tokens, success = obj.chatCompletion(context, fackData, False, bill) 16 | except Exception as e: 17 | print("chatCompletion failed 1 time:" + str(e)) 18 | try: 19 | message, total_tokens, success = obj.chatCompletion(context, fackData, False, bill) 20 | except Exception as e: 21 | print("chatCompletion failed 2 time:" + str(e)) 22 | traceback.print_exc() 23 | try: 24 | message, total_tokens, success = obj.chatCompletion(context, fackData, True, bill) 25 | except Exception as e: 26 | print("chatCompletion failed 2 time:" + str(e)) 27 | traceback.print_exc() 28 | raise Exception("服务异常,请重试。Service exception, please try again.") 29 | return message, total_tokens, success -------------------------------------------------------------------------------- /backend/app/pkgs/tools/llm_basic.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | import openai 4 | from app.pkgs.tools.llm_interface import LLMInterface 5 | from config import MODE, LLM_MODEL, GPT_KEYS 6 | 7 | api_keys = GPT_KEYS 8 | 9 | api_key_index = 0 10 | provider_index = 0 11 | lock = threading.Lock() 12 | 13 | def get_next_api_key(): 14 | print("get_next_api_key:", flush=True) 15 | print(api_keys, flush=True) 16 | global api_key_index, provider_index 17 | current_time = int(time.time()) 18 | with lock: 19 | provider = list(api_keys.keys())[provider_index] 20 | provider_data = api_keys[provider] 21 | keys = provider_data["keys"] 22 | key_data = keys[api_key_index] 23 | key = list(key_data.keys())[0] 24 | if current_time - key_data[key]["timestamp"] >= 80: 25 | key_data[key]["count"] = 0 26 | key_data[key]["timestamp"] = current_time 27 | api_key_index = (api_key_index + 1) % len(keys) 28 | if api_key_index == 0: 29 | provider_index = (provider_index + 1) % len(api_keys) 30 | return provider_data, key 31 | elif key_data[key]["count"] < 2: 32 | key_data[key]["count"] += 1 33 | api_key_index = (api_key_index + 1) % len(keys) 34 | if api_key_index == 0: 35 | provider_index = (provider_index + 1) % len(api_keys) 36 | return provider_data, key 37 | time.sleep(80) 38 | return get_next_api_key() 39 | 40 | class LLMBase(LLMInterface): 41 | def chatCompletion(self, context, fackData, use_backup_keys, bill): 42 | # Test frontend 43 | if MODE == "FAKE" and len(fackData) > 0: 44 | time.sleep(5) 45 | return fackData, True 46 | 47 | print("chatGPT - message:", flush=True) 48 | print(context, flush=True) 49 | provider_data, key = get_next_api_key() 50 | if len(key) < 10: 51 | print(f"\n\033[91mError: The GPT_KEYS({key}) in env.yaml is incorrectly, please modify the configuration according to the configuration notes. \033[0m\n") 52 | exit(1) 53 | 54 | openai.api_key = key 55 | openai.api_type = provider_data["api_type"] 56 | openai.api_version = provider_data["api_version"] 57 | openai.proxy = None if provider_data["proxy"]=="None" else provider_data["proxy"] 58 | openai_cli = openai.OpenAI(api_key=key, base_url=provider_data["api_base"]) 59 | print("chatGPT - get api key:"+openai.api_key, flush=True) 60 | print(f"provider_data:{provider_data}") 61 | 62 | try: 63 | response = openai_cli.chat.completions.create( 64 | model= LLM_MODEL, 65 | messages=context, 66 | max_tokens=10000, 67 | temperature=0, 68 | timeout=600 69 | ) 70 | 71 | total_tokens = response.usage.total_tokens 72 | response_text = response.choices[0].message.content 73 | print("chatGPT - response_text:"+response_text, flush=True) 74 | return response_text, total_tokens, True 75 | except Exception as e: 76 | msg = "\nError: Failed to access GPT, please check whether your network can connect to GPT and terminal proxy is running properly.\n" 77 | print(f"\033[91m{msg} \033[0m") 78 | raise e 79 | -------------------------------------------------------------------------------- /backend/app/pkgs/tools/llm_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | from config import GRADE 4 | 5 | class LLMInterface(ABC): 6 | @abstractmethod 7 | def chatCompletion(self, context): 8 | pass -------------------------------------------------------------------------------- /backend/app/pkgs/tools/llm_pro.py: -------------------------------------------------------------------------------- 1 | from app.pkgs.tools.llm_interface import LLMInterface 2 | 3 | class LLMPro(LLMInterface): 4 | def chatCompletion(self, context, fackData): 5 | # The current version does not support this feature 6 | return "", False -------------------------------------------------------------------------------- /backend/app/pkgs/tools/storage.py: -------------------------------------------------------------------------------- 1 | from flask import session 2 | 3 | def set(key, value): 4 | session[key] = value 5 | session.update 6 | 7 | def get(key): 8 | try: 9 | if key in session: 10 | return session[key] 11 | return None 12 | except Exception as e: 13 | return None 14 | 15 | def pop(key): 16 | session.pop(key) 17 | 18 | def clearup(): 19 | session.clear() -------------------------------------------------------------------------------- /backend/run.py: -------------------------------------------------------------------------------- 1 | from app.extensions import db 2 | from app.flask_ext import limiter_ip 3 | import datetime 4 | from app.controllers import register_controllers 5 | from flask import Flask, request 6 | from flask_cors import CORS 7 | from app.models.tenant_pro import Tenant 8 | from app.pkgs.tools import storage 9 | from app.pkgs.scheduler import register_job 10 | from config import APP_SECRET_KEY, BACKEND_DEBUG, BACKEND_HOST, BACKEND_PORT, AICODER_ALLOWED_ORIGIN, AUTO_LOGIN, GRADE 11 | 12 | import logging 13 | import os 14 | from apscheduler.schedulers.background import BackgroundScheduler 15 | 16 | aps_logger = logging.getLogger('apscheduler') 17 | aps_logger.setLevel(logging.ERROR) 18 | 19 | app = Flask(__name__) 20 | CORS(app) 21 | app.secret_key = APP_SECRET_KEY 22 | app.config.from_pyfile('config.py') 23 | 24 | NOT_CHECK_LOGIN_PATH = [ 25 | '/user/send_launch_code', 26 | '/user/language', 27 | '/user/change_language', 28 | '/user/login', 29 | '/user/changepassword', 30 | '/user/logout', 31 | '/user/register', 32 | '/pay/get_price', 33 | '/pay/send_pay', 34 | '/tenant/join', 35 | '/tenant/get_all_tenant', 36 | '/plugine/repo_analyzer', 37 | '/plugine/repo_analyzer_check', 38 | '/tencent/shop_create_item' 39 | ] 40 | 41 | 42 | @app.before_request 43 | def require_login(): 44 | # 开启自动登录,直接设置 45 | if AUTO_LOGIN: 46 | storage.set("username", "demo_user") 47 | storage.set("user_id", 1) 48 | storage.set("tenant_id", 0) 49 | 50 | path = request.path 51 | # 不需要验证登录状态的接口 52 | if path in NOT_CHECK_LOGIN_PATH: 53 | pass 54 | # 如果未登录返回错误 55 | elif not storage.get("username") or not storage.get("user_id"): 56 | return {'success': False, 'error': 'Access denied', 'code': 401} 57 | # 如果登录了,验证组织状态和是否有操作权限 58 | else: 59 | username = storage.get("username") 60 | current_time = datetime.datetime.now() 61 | args = request.get_data(as_text=True) 62 | print(f"req_time: {current_time}") 63 | print(f"req_user: {username}") 64 | print(f"req_path: {path}") 65 | print(f"req_args: {args}") 66 | 67 | if GRADE != "base": 68 | issuccess, msg, code = Tenant.check_status_and_role(request, path) 69 | if not issuccess: 70 | return {'success': False, 'error': msg, 'code': code} 71 | 72 | 73 | @app.after_request 74 | def after_request(response): 75 | origin = request.headers.get("Origin") 76 | if origin in AICODER_ALLOWED_ORIGIN: 77 | response.headers.add('Access-Control-Allow-Origin', origin) 78 | response.headers.add('Access-Control-Allow-Credentials', 'true') 79 | response.headers.add('Access-Control-Allow-Headers', 80 | 'Content-Type,Authorization') 81 | response.headers.add('Access-Control-Allow-Methods', 82 | 'GET,PUT,POST,DELETE,OPTIONS') 83 | return response 84 | 85 | 86 | limiter_ip.init_app(app) 87 | 88 | register_controllers(app) 89 | 90 | db.init_app(app) 91 | 92 | if os.environ.get('WERKZEUG_RUN_MAIN') != 'true' and GRADE != 'base': 93 | print("init scheduler") 94 | scheduler = BackgroundScheduler(daemon=True) 95 | register_job(scheduler, app) 96 | scheduler.start() 97 | 98 | if __name__ == '__main__': 99 | app.run(host=BACKEND_HOST, port=BACKEND_PORT, debug=BACKEND_DEBUG) 100 | -------------------------------------------------------------------------------- /backend/test.py: -------------------------------------------------------------------------------- 1 | # from app.models.app import App 2 | # from app.pkgs.devops.local_tools import compile_check 3 | from app.pkgs.tools.file_tool import read_file_content 4 | 5 | if __name__ == '__main__': 6 | # compile_check("./workspace/demo-task/java_demo_backend/", "1") 7 | isSuccess, content = read_file_content("./workspace/demo-task/java_demo_backend/build.cmd") 8 | print(isSuccess, content) 9 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the current commit SHA 4 | COMMIT_SHA=$(git rev-parse HEAD) 5 | 6 | # Get the current date in the format YYYYMMDD 7 | CURRENT_DATE=$(date +%Y%m%d) 8 | 9 | # Construct the new tag 10 | NEW_TAG="${CURRENT_DATE}-${COMMIT_SHA:0:7}" 11 | 12 | # Build the Docker image with the new tag 13 | docker build -t "${DOCKER_REPO}:${NEW_TAG}" -t "${DOCKER_REPO}:latest" -f Dockerfile . 14 | 15 | # Login to Docker Hub 16 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 17 | 18 | # Push the Docker image with the new tag 19 | docker push "${DOCKER_REPO}:${NEW_TAG}" 20 | docker push "${DOCKER_REPO}:latest" -------------------------------------------------------------------------------- /db/database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/db/database.db -------------------------------------------------------------------------------- /db/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade() -> None: 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade() -> None: 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /docs/CONFIG_CHANGE.md: -------------------------------------------------------------------------------- 1 | # 20230803 2 | ### APPS configuration(APP 字段配置) 3 | - remove service_structure 4 | - remove repos 5 | - rename project to service 6 | - change project type dict to list[dict] 7 | - add service.name(repo name) 8 | - rename project.project_base_prompt to service.base_prompt 9 | - rename project.project_info to service.intro 10 | - rename project.project_lib to service.lib 11 | - rename project.project_struct to service.struct 12 | - rename project.project_code_require service.specification 13 | - move api_doc_url to service.api_doc_url 14 | - move api_doc to service.api_doc -------------------------------------------------------------------------------- /docs/CONTACT.md: -------------------------------------------------------------------------------- 1 | ### 关注微信公众号,了解最新动态 2 | 3 | 4 | ### 添加小助手,加入微信互动群 5 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Thanks for contributing to DevOpsGPT 2 | To get started with contributing, please follow these steps: 3 | 4 | 1. Fork the repository and clone it to your local machine. 5 | 2. Create a new branch for your changes: `git checkout -b feat/my-branch-name` or `fix/my-branch-name`. 6 | 3. Make your desired changes or additions. 7 | 4. Run the tests to ensure everything is working as expected. 8 | 5. Commit your changes according to the [specification](https://www.conventionalcommits.org/en/v1.0.0/) : `git commit -m "(): "` For example: `feat(api): send an email to the customer when a product is shipped`. 9 | 6. Push to the branch: `git push origin feat/my-branch-name`. 10 | 7. Submit a pull request to the `master` branch of the original repository. -------------------------------------------------------------------------------- /docs/DOCUMENT_CN.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | 1. 克隆最新代码或选择已发布的版本,准备好 Python3.7 或以上版本。 4 | 2. 生成配置文件:复制 `env.yaml.tpl` 并重命名为 `env.yaml`。 5 | 3. 修改配置文件:编辑 `env.yaml`,添加GPT Token等必要信息。 6 | 4. 运行服务:在 Linux 或 Mac 上执行 `sh run.sh`,在 Windows 上双击运行 `run.bat`。 7 | 5. 访问服务:通过浏览器访问服务(启动日志中提供的访问地址,默认为 http://127.0.0.1:8080)。 8 | 6. 完成需求开发:按照页面引导完成需求开发,在 `./workspace` 目录下查看生成的代码。 9 | 10 | 11 | # 配置说明 12 | 13 | ### 基础配置类 14 | 15 | 1. FRONTEND_PORT、BACKEND_PORT:前端端口和后端端口 16 | 2. AICODER_ALLOWED_ORIGIN:后端允许跨域的地址,和前端访问地址保持一致。注意:如果你不使用127.0.0.1访问网站,请手动修改:frontend/static/js/coder.js 中的 apiUrl 17 | 3. LANGUAGE:语言 18 | 4. LLM_MODEL:模型 19 | 5. GPT_KEYS:GPT 的秘钥,配置 openai 和 azure 的接口信息(替换 sk-xxxx 为你的 key),如果不需要某个类型的接口,请将对应的元素整个删除掉(openai\azure),【注意】数组中最后一个元素后面不要加逗号、您可能需要开启全局代理来访问API接口 20 | 6. USERS:登录用户配置 21 | 22 | ### Git 配置 23 | 24 | DevOpsGPT 支持对接 Git,开启后,每次的开发任务可从 Git 拉取和推送代码 25 | 26 | 1. GIT_ENABLED: 是否启用 Git 27 | 2. GIT_URL: 配置你的 Git 地址,比如:https://github.com、https://gitlab.com 28 | 3. GIT_TOKEN: 配置你的 Git 令牌,可以从这里获取:https://github.com/settings/tokens、https://gitlab.com/-/profile/personal_access_tokens 29 | 4. GIT_USERNAME:Git 的登录用户名 30 | 5. GIT_EMAIL:Git 邮箱 31 | 6. APPS.service.git_path:应用对应的Git路径,包含group,比如:kuafuai/template_freestyleApp 32 | 33 | ### CI 持续集成工具配置 34 | 35 | DevOpsGPT 支持对接 GitlabCI、GithubActions 等 CI 工具,可以在代码提交后,触发您的流水线。 36 | 37 | 视频介绍:https://www.bilibili.com/video/BV1C8411R7HD 38 | 39 | 40 | 41 | 1. 完成以上“Git 配置” 42 | 2. GIT_API: 配置 Git API 的地址,比如:https://api.github.com 43 | 3. 如果是 Gitlab,你需要配置好流水线,比如:[.gitlab-ci.yml](https://github.com/kuafuai/template_javaWebApp_backend/blob/master/.gitlab-ci.yml)。同时你需要在 Gitlab 中配置好 Gitlab runner,细节请查看 [Gitlab 文档](https://docs.gitlab.com/runner/) 44 | 4. 如果你是 Github,你需要配置好流水线,比如:[default.yaml](https://github.com/kuafuai/template_javaWebApp_backend/blob/master/.github/workflows/default.yaml),细节请查看 [Github 文档](https://docs.github.com/en/actions/learn-github-actions) 45 | 46 | ### 自动化部署配置 47 | 48 | 自动化部署实现将开发好的应用程序一键部署到云服务中,供所有人访问和使用,真正实现从自然语言需求到可工作的软件! 49 | 50 | 视频介绍:https://www.bilibili.com/video/BV1cV4y1e7zg 51 | 52 | 以下以阿里云为例进行配置介绍,其它云平台类似,注意:使用云平台的资源可能会产生少量费用。 53 | 54 | 1. 在云平台上创建 AccessKey:鼠标移动到头像 - 选择 AccessKey 管理 - 创建 AccessKey 55 | 2. 根据以上创建好的 Kay,配置 CD_ACCESS_KEY 和 CD_SECRET_KEY 56 | 3. CD_REGION:设置部署的地域,比如部署到香港地区,可配置为:cn-hongkong,具体请咨询云平台客服 57 | 4. CD_EIP:在云平台上创建一个公网IP,用于访问公网(注意地域要和CD_REGION匹配) 58 | 5. CD_SECURITY:在云平台上创建一个安全组,用于在启动服务的时候开通外网方端口 59 | 6. CD_SWITCH:在云平台上创建一个交换机 60 | 61 | ### APPS 配置 62 | 63 | APPS 是我们需要开发的应用信息。在使用产品的第一步就是选择某个开发应用。在开发过程中需要根据这些信息来分析应用应该如何设计和开发。在开源版本中,这些信息需要手动维护,我们将在商业版中提供 AI 智能分析,自动生成相关信息。 64 | 65 | - app: 应用,包括多个服务,如:后端服务、前端服务、微服务 66 | - name、intro: 仅用于显示 67 | - service.name: 服务名称,保持唯一 68 | - service.git_workflow: Github 的 workflow 名字,当开启 Github CI 的时候才生效 69 | - service.git_path: git 路径,需要包含group,比如:kuafuai/template_freestyleApp 70 | - service.base_prompt: 基础起手 Prompt,会影响任务开发的效果 71 | - service.intro: 服务的基本信息 72 | - setpReqChooseLib(分析与服务信息一起使用的库包) 73 | - service.api_doc_url: 接口文档地址,用于动态获取接口文档 74 | - service.api_doc: 当前的接口文档 75 | - service.struct: 服务的文件目录结构信息 76 | - setp1Task(用于分子拆分任务) 77 | - service.lib: 服务可用的lib包 78 | - setpReqChooseLib(分析哪些库包与库列表一起使用) 79 | - service.specification: lib包使用规范 -------------------------------------------------------------------------------- /docs/files/WeChat-zhushou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/WeChat-zhushou.png -------------------------------------------------------------------------------- /docs/files/WeChat-微信-green.svg: -------------------------------------------------------------------------------- 1 | WeChat: 微信WeChat微信 -------------------------------------------------------------------------------- /docs/files/WeChat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/WeChat.jpg -------------------------------------------------------------------------------- /docs/files/ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/ci.png -------------------------------------------------------------------------------- /docs/files/demo-adduser-en.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/demo-adduser-en.jpeg -------------------------------------------------------------------------------- /docs/files/demo-adduser.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/demo-adduser.jpeg -------------------------------------------------------------------------------- /docs/files/demo-gptmeeting.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/demo-gptmeeting.jpeg -------------------------------------------------------------------------------- /docs/files/demo-jiqiren.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/demo-jiqiren.jpeg -------------------------------------------------------------------------------- /docs/files/document-English-blue.svg: -------------------------------------------------------------------------------- 1 | document: EnglishdocumentEnglish -------------------------------------------------------------------------------- /docs/files/intro-flow-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/intro-flow-en.png -------------------------------------------------------------------------------- /docs/files/intro-flow-simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/intro-flow-simple.png -------------------------------------------------------------------------------- /docs/files/intro-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/docs/files/intro-flow.png -------------------------------------------------------------------------------- /docs/files/ドキュメント-日本語-blue.svg: -------------------------------------------------------------------------------- 1 | ドキュメント: 日本語ドキュメント日本語 2 | -------------------------------------------------------------------------------- /docs/files/官网-企业版-purple.svg: -------------------------------------------------------------------------------- 1 | 官网: 企业版官网企业版 -------------------------------------------------------------------------------- /docs/files/文档-中文版-blue.svg: -------------------------------------------------------------------------------- 1 | 文档: 中文版文档中文版 -------------------------------------------------------------------------------- /env.yaml.tpl: -------------------------------------------------------------------------------- 1 | # Configure the interface information of openai and azure. If a certain type of interface is not needed, please completely delete the corresponding element (openai\azure). 2 | # 配置 openai 和 azure 的接口信息(替换 sk-xxxx 为你的 key),如果不需要某个类型的接口,请将对应的元素整个删除掉(openai\azure),【注意】数组中最后一个元素后面不要加逗号、您可能需要开启全局代理来访问API接口 3 | GPT_KEYS: | 4 | { 5 | "openai": { 6 | "keys": [ 7 | {"sk-xxxx": {"count": 0, "timestamp": 0}} 8 | ], 9 | "api_type": "open_ai", 10 | "api_base": "https://api.openai.com/v1", 11 | "api_version": "2020-11-07", 12 | "proxy": "None" 13 | } 14 | , 15 | "azure": { 16 | "keys": [ 17 | {"sk-xxxx": {"count": 0, "timestamp": 0}} 18 | ], 19 | "api_type": "azure", 20 | "api_base": "https://example-gpt.openai.azure.com/", 21 | "api_version": "2023-05-15", 22 | "deployment_id": "deployment-name", 23 | "proxy": "None" 24 | } 25 | } 26 | 27 | GPT_KEYS_BACKUP: | 28 | { 29 | "openai": { 30 | "keys": [ 31 | {"sk-xxxx": {"count": 0, "timestamp": 0}} 32 | ], 33 | "api_type": "open_ai", 34 | "api_base": "https://api.openai.com/v1", 35 | "api_version": "2020-11-07", 36 | "proxy": "None" 37 | } 38 | , 39 | "azure": { 40 | "keys": [ 41 | {"sk-xxxx": {"count": 0, "timestamp": 0}} 42 | ], 43 | "api_type": "azure", 44 | "api_base": "https://example-gpt.openai.azure.com/", 45 | "api_version": "2023-05-15", 46 | "deployment_id": "deployment-name", 47 | "proxy": "None" 48 | } 49 | } 50 | 51 | # Configure the model used (do not use less than 16k token model), [note] openai plus members and API members are different, you opena plus member does not mean that you can use gpt4 model, specifically consult the official documentation of openai 52 | # 配置使用的模型(不要使用小于16k token的模型),【注意】openai的plus会员和API会员是不同的,你开通了plus会员不代表可以用gpt4的模型,具体查阅openai的官方文档 53 | LLM_MODEL: "gpt-3.5-turbo-16k-0613" 54 | 55 | LANGUAGE: 'en' # en、zh 56 | SITE_NAME: 'DevOpsGPT' 57 | INVITATION_CODE: '' 58 | FRONTEND_PORT: 8080 59 | BACKEND_PORT: 8081 60 | # The url that the back end allows cross-domain access (front-end access address) 61 | # 后端允许跨域访问的url(前端访问地址) 62 | AICODER_ALLOWED_ORIGIN: '["http://127.0.0.1:8080"]' 63 | BACKEND_URL: 'http://127.0.0.1:8081' 64 | 65 | BACKEND_HOST: '0.0.0.0' 66 | BACKEND_DEBUG: true 67 | APP_SECRET_KEY: 'Es*lsnGptDevOps' 68 | WORKSPACE_PATH: './workspace/' 69 | SQLALCHEMY_DATABASE_URI: 'sqlite:///../db/database.db' 70 | MODE: "REAL" # FAKE、REAL 71 | GRADE: "base" 72 | 73 | CD_TOOLS: "local" # local、aliyun Open source version only supports Alibaba Cloud 当前开源版只支持阿里云 74 | CD_ACCESS_KEY: "" 75 | CD_SECRET_KEY: "" 76 | 77 | DEVOPS_TOOLS: "local" # local、gitlab、github Please refer to the official documentation of the tool to learn how to use it. 请查阅相关工具的官方文档了解如何使用 78 | GIT_ENABLED: true # Whether to enable Git. If yes, pull code from Git(Note APPS.service.git_path configuration item). 是否开启Git,如果开启将从Git中拉代码(注意 APPS.service.git_path 配置项) 79 | GIT_URL: "https://github.com" # https://github.com、https://gitlab.com 80 | GIT_API: "https://api.github.com" # https://api.github.com 81 | GIT_TOKEN: "xxxx" # Get from here https://github.com/settings/tokens、https://gitlab.com/-/profile/personal_access_tokens 82 | GIT_USERNAME: "xxxx" 83 | GIT_EMAIL: "xxxx@x.x" 84 | GITHUB_PROXY: "" 85 | 86 | EMAIL_SERVER: "" 87 | EMAIL_PORT: "" 88 | EMAIL_SSL: true 89 | EMAIL_SENDER: "" 90 | EMAIL_PASSWORD: "" 91 | 92 | PAYPAL_MODE: "sandbox" # sandbox or live 93 | PAYPAL_ID: "xxxx" 94 | PAYPAL_SECRET: "xxxx" 95 | ALIPAY_SERVER: "https://openapi-sandbox.dl.alipaydev.com/gateway.do" # sandbox or live 96 | ALIPAY_ID: "666" 97 | ALIPAY_PRIVATE_KEY: "xxxx" 98 | ALIPAY_PUBLIC_KEY: "xxxx" 99 | 100 | AUTO_LOGIN: true 101 | USERS: | 102 | { 103 | "demo_user": "123456" 104 | } -------------------------------------------------------------------------------- /frontend/Semantic UI_files/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/Semantic UI_files/book.png -------------------------------------------------------------------------------- /frontend/Semantic UI_files/devices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/Semantic UI_files/devices.png -------------------------------------------------------------------------------- /frontend/Semantic UI_files/easing.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Open source under the BSD License. 5 | * 6 | * Copyright © 2001 Robert Penner 7 | * All rights reserved. 8 | * 9 | */ 10 | jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g 3 | -------------------------------------------------------------------------------- /frontend/Semantic UI_files/theming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/Semantic UI_files/theming.png -------------------------------------------------------------------------------- /frontend/Semantic UI_files/toolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/Semantic UI_files/toolbox.png -------------------------------------------------------------------------------- /frontend/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/favicon.ico -------------------------------------------------------------------------------- /frontend/privacy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 30 | 31 |
32 |
33 | 隐私权政策
34 | 欢迎使用我们的插件。在使用我们的产品之前,请仔细阅读本隐私政策,以了解我们如何收集、使用和共享您的个人信息。如果您对本隐私政策有任何疑问,请随时与我们联系。
35 | 收集的信息
36 | 我们的插件可能会收集您的IP地址以及您访问的网站内容。这些信息仅用于内部分析和改善我们的服务质量。我们不会将您的IP地址与您的个人身份信息进行关联。
37 | 使用信息
38 | 我们仅使用收集的信息来提供、维护和改进我们的服务。收集的数据将帮助我们更好地理解用户的需求,并根据用户的喜好和行为提供个性化的体验。我们不会出售、出租或以任何方式泄露您的个人信息。
39 | 共享信息
40 | 我们不会与任何第三方共享您的个人信息,除非获得您的明确同意或根据法律规定。我们可能与合作伙伴共享匿名的分析数据,用于改进服务质量和用户体验。
41 | 数据安全
42 | 我们致力于保护您的个人信息安全。为此,我们采取了各种安全措施来防止未经授权的访问、使用或披露。尽管我们采取了合理的安全措施,但请注意,没有任何互联网传输或电子存储方法是100%安全的。
43 | 变更通知
44 | 我们可能会不时更新本隐私政策。任何修改都会在本页面上发布,并在修改生效之前通知您。建议您定期查看本隐私政策,以了解任何变更。如果您继续使用我们的服务,则视为您同意遵守修订后的隐私政策。
45 | 联系我们
46 | 如果您对本隐私政策有任何疑问或意见,请通过以下联系方式与我们联系:
47 | service@kuafuai.net
48 | 感谢您阅读我们的隐私政策。
49 | 
50 | Privacy Policy
51 | Welcome to our plugin. Before using our product, please read this privacy policy carefully to understand how we collect, use, and share your personal information. If you have any questions about this privacy policy, please feel free to contact us.
52 | Information Collected
53 | Our plugin may collect your IP address and the content of the websites you visit. This information is used solely for internal analysis and to improve the quality of our service. We will not link your IP address to your personal identity.
54 | Use of Information
55 | We only use the collected information to provide, maintain, and improve our services. The collected data helps us better understand users' needs and provide a personalized experience based on user preferences and behavior. We will not sell, rent, or disclose your personal information in any way.
56 | Sharing of Information
57 | We will not share your personal information with any third parties unless we obtain your explicit consent or as required by law. We may share anonymous analytical data with partners to improve the quality of our service and user experience.
58 | Data Security
59 | We are committed to protecting the security of your personal information. To this end, we have implemented various security measures to prevent unauthorized access, use, or disclosure. Despite taking reasonable security measures, please note that no method of Internet transmission or electronic storage is 100% secure.
60 | Changes Notification
61 | We may update this privacy policy from time to time. Any changes will be posted on this page and will be effective upon the modification's announcement. We recommend that you regularly review this privacy policy to be aware of any changes. By continuing to use our services, you agree to comply with the revised privacy policy.
62 | Contact Us
63 | If you have any questions or concerns about this privacy policy, please contact us at:
64 | service@kuafuai.net
65 | Thank you for reading our privacy policy.
66 | 
67 |
68 | 69 | 74 | 75 |
76 | @
77 | 78 | 79 | -------------------------------------------------------------------------------- /frontend/requirement.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 38 | 39 |
40 | 50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 |
67 | 68 | 73 | 74 | 92 | 93 | 94 |
95 | @
96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /frontend/static/css/darcula.min.css: -------------------------------------------------------------------------------- 1 | .cm-s-darcula{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.cm-s-darcula.CodeMirror{background:#2b2b2b;color:#a9b7c6}.cm-s-darcula span.cm-meta{color:#bbb529}.cm-s-darcula span.cm-number{color:#6897bb}.cm-s-darcula span.cm-keyword{color:#cc7832;line-height:1em;font-weight:700}.cm-s-darcula span.cm-def{color:#a9b7c6;font-style:italic}.cm-s-darcula span.cm-variable{color:#a9b7c6}.cm-s-darcula span.cm-variable-2{color:#a9b7c6}.cm-s-darcula span.cm-variable-3{color:#9876aa}.cm-s-darcula span.cm-type{color:#abc;font-weight:700}.cm-s-darcula span.cm-property{color:#ffc66d}.cm-s-darcula span.cm-operator{color:#a9b7c6}.cm-s-darcula span.cm-string{color:#6a8759}.cm-s-darcula span.cm-string-2{color:#6a8759}.cm-s-darcula span.cm-comment{color:#61a151;font-style:italic}.cm-s-darcula span.cm-link{color:#cc7832}.cm-s-darcula span.cm-atom{color:#cc7832}.cm-s-darcula span.cm-error{color:#bc3f3c}.cm-s-darcula span.cm-tag{color:#629755;font-weight:700;font-style:italic;text-decoration:underline}.cm-s-darcula span.cm-attribute{color:#6897bb}.cm-s-darcula span.cm-qualifier{color:#6a8759}.cm-s-darcula span.cm-bracket{color:#a9b7c6}.cm-s-darcula span.cm-builtin{color:#ff9e59}.cm-s-darcula span.cm-special{color:#ff9e59}.cm-s-darcula span.cm-matchhighlight{color:#fff;background-color:rgba(50,89,48,.7);font-weight:400}.cm-s-darcula span.cm-searching{color:#fff;background-color:rgba(61,115,59,.7);font-weight:400}.cm-s-darcula .CodeMirror-cursor{border-left:1px solid #a9b7c6}.cm-s-darcula .CodeMirror-activeline-background{background:#323232}.cm-s-darcula .CodeMirror-gutters{background:#313335;border-right:1px solid #313335}.cm-s-darcula .CodeMirror-guttermarker{color:#ffee80}.cm-s-darcula .CodeMirror-guttermarker-subtle{color:#d0d0d0}.cm-s-darcula .CodeMirrir-linenumber{color:#606366}.cm-s-darcula .CodeMirror-matchingbracket{background-color:#3b514d;color:#ffef28!important;font-weight:700}.cm-s-darcula div.CodeMirror-selected{background:#214283}.CodeMirror-hints.darcula{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;color:#9c9e9e;background-color:#3b3e3f!important}.CodeMirror-hints.darcula .CodeMirror-hint-active{background-color:#494d4e!important;color:#9c9e9e!important} -------------------------------------------------------------------------------- /frontend/static/css/themes/default/assets/fonts/brand-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/css/themes/default/assets/fonts/brand-icons.woff2 -------------------------------------------------------------------------------- /frontend/static/css/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/css/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /frontend/static/css/themes/default/assets/fonts/outline-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/css/themes/default/assets/fonts/outline-icons.woff2 -------------------------------------------------------------------------------- /frontend/static/css/themes/default/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/css/themes/default/assets/images/flags.png -------------------------------------------------------------------------------- /frontend/static/image/WeChat-zhushou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/WeChat-zhushou.png -------------------------------------------------------------------------------- /frontend/static/image/WeChat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/WeChat.jpg -------------------------------------------------------------------------------- /frontend/static/image/i_cloudwork_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/i_cloudwork_logo.png -------------------------------------------------------------------------------- /frontend/static/image/i_cloudwork_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/i_cloudwork_show.png -------------------------------------------------------------------------------- /frontend/static/image/i_cloudwork_wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/i_cloudwork_wechat.png -------------------------------------------------------------------------------- /frontend/static/image/index_chanpinjietu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/index_chanpinjietu.png -------------------------------------------------------------------------------- /frontend/static/image/index_demo_chatbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/index_demo_chatbot.png -------------------------------------------------------------------------------- /frontend/static/image/index_demo_crud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/index_demo_crud.png -------------------------------------------------------------------------------- /frontend/static/image/index_demo_more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/index_demo_more.png -------------------------------------------------------------------------------- /frontend/static/image/index_header.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/index_header.jpeg -------------------------------------------------------------------------------- /frontend/static/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/logo.png -------------------------------------------------------------------------------- /frontend/static/image/models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/models.png -------------------------------------------------------------------------------- /frontend/static/image/role_op.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_op.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_op的副本.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_op的副本.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_pm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_pm.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_pm的副本.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_pm的副本.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_qa.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_qa.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_qa的副本.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_qa的副本.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_rd.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_rd.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_rd的副本.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_rd的副本.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_tl.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_tl.jpeg -------------------------------------------------------------------------------- /frontend/static/image/role_tl的副本.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/frontend/static/image/role_tl的副本.jpeg -------------------------------------------------------------------------------- /frontend/static/js/index.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | // show dropdown on hover 3 | $('.ui.dropdown').dropdown({ 4 | on: 'hover' 5 | }); 6 | 7 | const url = window.location; 8 | const path = url.pathname; 9 | 10 | getRechargeList() 11 | }); 12 | 13 | function getRechargeList() { 14 | var requestData = {} 15 | 16 | successCallback = function(data) { 17 | plus = data.data.plus 18 | $("#intro_pricing_free").text(plus.FREE.suffix+plus.FREE.value) 19 | $("#intro_pricing_basic").text(plus.BASIC_MONTHLY.suffix+plus.BASIC_MONTHLY.value) 20 | $("#intro_pricing_pro").text(plus.PRO_MONTHLY.suffix+plus.PRO_MONTHLY.value) 21 | 22 | $("#intro_pricing_free_intro").html(plus.FREE.intro) 23 | $("#intro_pricing_basic_intro").html(plus.BASIC_MONTHLY.intro) 24 | $("#intro_pricing_pro_intro").html(plus.PRO_MONTHLY.intro) 25 | } 26 | 27 | sendAjaxRequest('/pay/get_price', 'GET', requestData, successCallback, alertErrorCallback, true, false) 28 | } 29 | 30 | function gotoRegister() { 31 | url = "/user_register.html?email=" + $("#register-email").val() 32 | openUrl(url) 33 | } -------------------------------------------------------------------------------- /frontend/static/js/requirement.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | getRequirementList() 3 | 4 | // show dropdown on hover 5 | $('.main.menu .ui.dropdown').dropdown({ 6 | on: 'hover' 7 | }); 8 | }); 9 | 10 | function getRequirementList() { 11 | requestData = '' 12 | 13 | successCallback = function(data) { 14 | requirements = data.data.requirements 15 | 16 | var str = "" 17 | 18 | requirements.forEach(function (requirement, element_index, element_array) { 19 | str += ` 20 | `+requirement["requirement_name"]+``+requirement["status"]+` 21 |
`+requirement["created_at"]+`
22 | 23 | 24 | 25 | `+requirement["username"]+` 26 | 27 |
28 |
29 | 30 | ` 31 | $("#app_list").html(str) 32 | }); 33 | 34 | $('.ui.rating').rating({ 35 | maxRating: 5, 36 | onRate(newValue){ 37 | rid = $(this).attr('rid') 38 | rkey = $(this).attr('rkey') 39 | data = {} 40 | data[rkey] = newValue 41 | updateRequirment(rid, data) 42 | } 43 | }); 44 | } 45 | 46 | sendAjaxRequest('/requirement/get', 'GET', requestData, successCallback, alertErrorCallback, true, false) 47 | } 48 | 49 | function showRequirement(requirement_id) { 50 | window.location.href = "/task.html?task_id="+requirement_id 51 | } 52 | 53 | function updateRequirment(requirement_id, data) { 54 | var requestData = JSON.stringify({ 'requirement_id': requirement_id, data }) 55 | 56 | successCallback = function(data) { 57 | } 58 | 59 | errorCallback = function(data) { 60 | } 61 | 62 | sendAjaxRequest('/requirement/update', 'POST', requestData, successCallback, errorCallback, true, false) 63 | } -------------------------------------------------------------------------------- /frontend/static/js/user.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | // show dropdown on hover 3 | $('.main.menu .ui.dropdown').dropdown({ 4 | on: 'hover' 5 | }); 6 | 7 | var queryString = window.location.search; 8 | var params = new URLSearchParams(queryString); 9 | $("#login-email").val(params.get('email')); 10 | 11 | $("#login-password").keydown(function (event) { 12 | if (event.keyCode === 13) { 13 | // 在这里执行回车键被按下时的操作 14 | login() 15 | } 16 | }); 17 | }); 18 | 19 | function login() { 20 | var requestData = JSON.stringify({ 'username': $("#login-username").val(), 'password': $("#login-password").val() }) 21 | 22 | successCallback = function(data) { 23 | window.location.href = "task.html"; 24 | } 25 | 26 | errorCallback = function(error) { 27 | $("#login-message").html(error) 28 | $("#login-message").fadeOut().fadeIn() 29 | 30 | setTimeout(function () { 31 | $("#login-message").fadeOut(); 32 | }, 4000); 33 | } 34 | 35 | sendAjaxRequest('/user/login', "POST", requestData, successCallback, errorCallback, true, false) 36 | } 37 | 38 | function register() { 39 | var requestData = JSON.stringify({ 40 | 'username': $("#login-username").val(), 41 | 'password': $("#login-password").val(), 42 | 'email': $("#login-email").val(), 43 | 'phone': $("#login-phone").val(), 44 | 'launch_code': $("#login-launch_code").val(), 45 | 'invitation_code': $("#login-invitation-code").val(), 46 | }) 47 | 48 | successCallback = function(data) { 49 | window.location.href = "user_login.html"; 50 | } 51 | 52 | errorCallback = function(error) { 53 | $("#login-message").html(error) 54 | $("#login-message").fadeOut().fadeIn() 55 | 56 | setTimeout(function () { 57 | $("#login-message").fadeOut(); 58 | }, 4000); 59 | } 60 | 61 | sendAjaxRequest('/user/register', "POST", requestData, successCallback, errorCallback, true, false) 62 | } 63 | 64 | function changePassword() { 65 | var requestData = JSON.stringify({ 66 | 'password': $("#login-password").val(), 67 | 'phone': $("#login-phone").val(), 68 | 'launch_code': $("#login-launch_code").val(), 69 | }) 70 | 71 | successCallback = function(data) { 72 | window.location.href = "user_login.html"; 73 | } 74 | 75 | errorCallback = function(error) { 76 | $("#login-message").html(error) 77 | $("#login-message").fadeOut().fadeIn() 78 | 79 | setTimeout(function () { 80 | $("#login-message").fadeOut(); 81 | }, 4000); 82 | } 83 | 84 | sendAjaxRequest('/user/changepassword', "POST", requestData, successCallback, errorCallback, true, false) 85 | } 86 | 87 | function send_launch_code(ele, code_type) { 88 | $(ele).addClass("disabled") 89 | $(ele).addClass("loading") 90 | 91 | var requestData = JSON.stringify({ 92 | 'phone': $("#login-phone").val(), 93 | 'code_type': code_type 94 | }) 95 | 96 | successCallback = function(data) { 97 | $(ele).removeClass("loading") 98 | } 99 | 100 | errorCallback = function(error) { 101 | $("#login-message").html(error) 102 | $("#login-message").fadeOut().fadeIn() 103 | $(ele).removeClass("loading") 104 | $(ele).removeClass("disabled") 105 | 106 | setTimeout(function () { 107 | $("#login-message").fadeOut(); 108 | }, 4000); 109 | } 110 | 111 | sendAjaxRequest('/user/send_launch_code', "POST", requestData, successCallback, errorCallback, true, false) 112 | } -------------------------------------------------------------------------------- /frontend/tenant.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 44 | 45 |
46 | 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 |
75 | 76 | 81 | 82 |
83 | @
84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /frontend/user_changepassword.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 38 | 39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 |
64 | 65 | 66 | 71 | 72 |
73 | @
74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /frontend/user_login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 38 | 39 |
40 |
41 |
42 | 43 | 44 |
45 |
46 | 47 | 48 |
49 |
50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 | 59 | 60 | 65 | 66 |
67 | @
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /frontend/user_register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 38 | 39 |
40 |
41 | 45 |
46 | 47 | 48 |
49 |
50 | 51 | 52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 | 78 | 79 | 84 | 85 |
86 | @
87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /i18n/README.md: -------------------------------------------------------------------------------- 1 | # controllers 2 | xgettext -k_ -o i18n/controllers.pot backend/app/controllers/*.py 3 | # for init 4 | msginit -l i18n/zh_controllers -i i18n/controllers.pot 5 | msginit -l i18n/en_controllers -i i18n/controllers.pot 6 | # for update 7 | msgmerge -U i18n/zh_controllers.po i18n/controllers.pot 8 | msgmerge -U i18n/en_controllers.po i18n/controllers.pot 9 | # gen po 10 | msgfmt -o i18n/zh/LC_MESSAGES/controllers.mo i18n/zh_controllers.po 11 | msgfmt -o i18n/en/LC_MESSAGES/controllers.mo i18n/en_controllers.po 12 | 13 | 14 | # frontend 15 | xgettext -k_ -o i18n/frontend.pot backend/app/pkgs/tools/i18b.py 16 | # for init 17 | msginit -l i18n/zh_frontend -i i18n/frontend.pot 18 | msginit -l i18n/en_frontend -i i18n/frontend.pot 19 | # for update 20 | msgmerge -U i18n/zh_frontend.po i18n/frontend.pot 21 | msgmerge -U i18n/en_frontend.po i18n/frontend.pot 22 | # gen po 23 | msgfmt -o i18n/zh/LC_MESSAGES/frontend.mo i18n/zh_frontend.po 24 | msgfmt -o i18n/en/LC_MESSAGES/frontend.mo i18n/en_frontend.po 25 | 26 | # prompt 27 | xgettext -k_ -o i18n/prompt.pot backend/app/pkgs/prompt/*.py 28 | # for init 29 | msginit -l i18n/zh_prompt -i i18n/prompt.pot 30 | msginit -l i18n/en_prompt -i i18n/prompt.pot 31 | # for update 32 | msgmerge -U i18n/zh_prompt.po i18n/prompt.pot 33 | msgmerge -U i18n/en_prompt.po i18n/prompt.pot 34 | # gen po 35 | msgfmt -o i18n/zh/LC_MESSAGES/prompt.mo i18n/zh_prompt.po 36 | msgfmt -o i18n/en/LC_MESSAGES/prompt.mo i18n/en_prompt.po -------------------------------------------------------------------------------- /i18n/en/LC_MESSAGES/controllers.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/en/LC_MESSAGES/controllers.mo -------------------------------------------------------------------------------- /i18n/en/LC_MESSAGES/frontend.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/en/LC_MESSAGES/frontend.mo -------------------------------------------------------------------------------- /i18n/en/LC_MESSAGES/prompt.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/en/LC_MESSAGES/prompt.mo -------------------------------------------------------------------------------- /i18n/en_prompt.po: -------------------------------------------------------------------------------- 1 | # Language i18n/en translations for PACKAGE package. 2 | # Copyright (C) 2023 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # booboo , 2023. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-07-18 14:25+0800\n" 11 | "PO-Revision-Date: 2023-07-18 14:26+0800\n" 12 | "Last-Translator: booboo \n" 13 | "Language-Team: Language i18n/en\n" 14 | "Language: i18n/en_prompt\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: backend/app/pkgs/prompt/requirement.py:68 20 | msgid "" 21 | "Prerequisites, Detailed Operation Steps, Expected Results, Other Explanatory " 22 | "Notes." 23 | msgstr "" 24 | -------------------------------------------------------------------------------- /i18n/prompt.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-07-18 14:25+0800\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: backend/app/pkgs/prompt/requirement.py:68 21 | msgid "" 22 | "Prerequisites, Detailed Operation Steps, Expected Results, Other Explanatory " 23 | "Notes." 24 | msgstr "" 25 | -------------------------------------------------------------------------------- /i18n/zh/LC_MESSAGES/controllers.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/zh/LC_MESSAGES/controllers.mo -------------------------------------------------------------------------------- /i18n/zh/LC_MESSAGES/frontend.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/zh/LC_MESSAGES/frontend.mo -------------------------------------------------------------------------------- /i18n/zh/LC_MESSAGES/prompt.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/i18n/zh/LC_MESSAGES/prompt.mo -------------------------------------------------------------------------------- /i18n/zh_prompt.po: -------------------------------------------------------------------------------- 1 | # Language i18n/zh translations for PACKAGE package. 2 | # Copyright (C) 2023 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # booboo , 2023. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-07-18 14:25+0800\n" 11 | "PO-Revision-Date: 2023-07-18 14:25+0800\n" 12 | "Last-Translator: booboo \n" 13 | "Language-Team: Language i18n/zh\n" 14 | "Language: i18n/zh_prompt\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: backend/app/pkgs/prompt/requirement.py:68 20 | msgid "" 21 | "Prerequisites, Detailed Operation Steps, Expected Results, Other Explanatory " 22 | "Notes." 23 | msgstr "前置条件、详细操作步骤、预期结果、其他解释说明" 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask-sqlalchemy==3.1.1 2 | flask-cors==3.0.10 3 | flask==2.2.5 4 | pyyaml 5 | python-gitlab 6 | openai==1.8.0 7 | aliyun-python-sdk-core 8 | aliyun-python-sdk-eci 9 | alembic 10 | paypalrestsdk 11 | alipay-sdk-python==3.6.640 12 | typed_ast 13 | click 14 | httpx 15 | alibabacloud_dysmsapi20170525 16 | certifi 17 | apscheduler 18 | flask-limiter 19 | boto3 20 | # yum install mysql-devel gcc gcc-devel python-devel 21 | # mysqlclient 22 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | call :get_python3 5 | 6 | if errorlevel 1 ( 7 | echo Python 3.7 or higher is required to run DevOpsGPT. 8 | exit /b 1 9 | ) 10 | 11 | copy .\.github\hooks\pre-commit .\.git\hooks\ 12 | 13 | echo Installing missing packages... 14 | call %PYTHON_CMD% -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 15 | 16 | start "" %PYTHON_CMD% backend\run.py 17 | 18 | call :start_frontend %PYTHON_CMD% 19 | 20 | call :kill_by_port %%BACKEND_PORT%% 21 | 22 | exit /b 0 23 | 24 | REM function 25 | :get_python3 26 | for /f "delims=" %%i in ('where python 2^>nul') do ( 27 | set "PYTHON_CMD=%%i" 28 | goto :pyCheck 29 | ) 30 | :pyCheck 31 | if not defined PYTHON_CMD ( 32 | echo Python3 not found. Please install Python3.7 or higher. 33 | exit /b 1 34 | ) 35 | GOTO:EOF 36 | 37 | :get_config_value 38 | set "yaml_file=env.yaml" 39 | set "key=%~1" 40 | 41 | for /f "usebackq tokens=2 delims=: " %%a in (`findstr /C:"%key%:" "%yaml_file%"`) do ( 42 | set "value=%%a" 43 | ) 44 | 45 | if not defined value ( 46 | echo Error: Key '%key%' not found in config file '%yaml_file%'. Please copy a new env.yaml from env.yaml.tpl and reconfigure it according to the documentation. 47 | exit /b 1 48 | ) 49 | set "%key%=!value!" 50 | GOTO:EOF 51 | 52 | :start_frontend 53 | set "PYTHON_CMD=%~1" 54 | call :get_config_value FRONTEND_PORT 55 | call :get_config_value BACKEND_PORT 56 | if %errorlevel%==1 ( 57 | pause 58 | exit /b 1 59 | ) 60 | 61 | REM Wait for the backend service to start 62 | for /l %%i in (1, 1, 20) do ( 63 | set "response=" 64 | for /f "delims=" %%a in ('set HTTP_PROXY= ^& set HTTPS_PROXY= ^& set ALL_PROXY= ^& set http_proxy= ^& set https_proxy= ^& set all_proxy= ^& curl -s -o nul -w "%%{http_code}" http://127.0.0.1:%BACKEND_PORT%') do set "response=%%a" 65 | 66 | echo !response! 67 | if "!response!"=="404" ( 68 | echo. 69 | echo Service started successfully, please use a browser to visit: http://127.0.0.1:!FRONTEND_PORT! 70 | GOTO :starFrontend 71 | ) else ( 72 | timeout /t 5 > nul 73 | ) 74 | ) 75 | :starFrontend 76 | call %PYTHON_CMD% -m http.server %FRONTEND_PORT% --directory frontend 77 | GOTO:EOF 78 | 79 | :kill_by_port 80 | set "port=%~1" 81 | for /f "delims=" %%a in ('netstat -aon ^| findstr "LISTENING" ^| findstr ":!port!"') do ( 82 | set "line=%%a" 83 | for /f "tokens=5" %%b in ("!line!") do set "pid=%%b" 84 | ) 85 | 86 | if not defined pid ( 87 | echo The port is not in use: %port% 88 | exit /b 1 89 | ) 90 | 91 | taskkill /f /pid %pid% 92 | GOTO:EOF 93 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function get_python3() { 4 | if command -v python3 &> /dev/null 5 | then 6 | echo "python3" 7 | else 8 | echo "Python3 not found. Please install Python3.7 or higher." 9 | exit 1 10 | fi 11 | } 12 | 13 | function get_config_value() { 14 | local yaml_file="env.yaml" 15 | local key="$1" 16 | 17 | local value=$(grep "$key:" "$yaml_file" | awk -F': ' '{print $2}') 18 | 19 | if [[ -z "$value" ]]; then 20 | echo "Error: Key '$key' not found in config file '$yaml_file'. Please copy a new env.yaml from env.yaml.tpl and reconfigure it according to the documentation." 21 | exit 1 22 | fi 23 | 24 | echo "$value" 25 | } 26 | 27 | function start_frontend() { 28 | PYTHON_CMD="$1" 29 | frontend_port=$(get_config_value "FRONTEND_PORT") 30 | $PYTHON_CMD -m http.server $frontend_port --directory frontend & 31 | 32 | echo $! 33 | 34 | backend_port=$(get_config_value "BACKEND_PORT") 35 | for ((i=1; i<=20; i++)) 36 | do 37 | response=$(HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= curl -s -o /dev/null -w "%{http_code}" 127.0.0.1:$backend_port) 38 | 39 | if [ "$response" = "404" ] || [ "$response" = "200" ]; then 40 | echo -e "\n\nService started successfully, please use browser to visit: http://127.0.0.1:$frontend_port" 41 | break 42 | else 43 | sleep 5 44 | fi 45 | done 46 | } 47 | 48 | function kill_by_port() { 49 | port="$1" 50 | os_type=$(uname -s) 51 | case "$os_type" in 52 | Linux*) 53 | pid=$(lsof -t -i ":$port") 54 | ;; 55 | Darwin*) 56 | pid=$(lsof -t -i ":$port") 57 | ;; 58 | *) 59 | echo "unknown os type: $os_type" 60 | exit 1 61 | ;; 62 | esac 63 | if [ -z "$pid" ]; then 64 | echo "The port is not in use: $port" 65 | else 66 | echo "will kill port $port pid $pid" 67 | kill -9 $pid 68 | fi 69 | } 70 | 71 | cp .github/hooks/pre-commit .git/hooks/ 72 | 73 | kill_by_port $(get_config_value "FRONTEND_PORT") 74 | kill_by_port $(get_config_value "BACKEND_PORT") 75 | 76 | PYTHON_CMD=$(get_python3) 77 | 78 | if $PYTHON_CMD -c "import sys; sys.exit(sys.version_info < (3, 7))"; then 79 | echo Installing missing packages... 80 | $PYTHON_CMD -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 81 | else 82 | echo "Python 3.7 or higher is required to run DevOpsGPT." 83 | exit 1 84 | fi 85 | 86 | # start the frontend service 87 | start_frontend $PYTHON_CMD & 88 | 89 | # start the backend service 90 | $PYTHON_CMD backend/run.py 91 | 92 | # or run the `ps -ef | grep 8080` command to get the PID and `kill PID` to stop 93 | kill_by_port $(get_config_value "FRONTEND_PORT") 94 | -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/bootstrap/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.6.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2022 The Bootstrap Authors 4 | * Copyright 2011-2022 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/css/sidepanel.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | 7 | #sidepanel{ 8 | display: none; 9 | } 10 | 11 | .all { 12 | width: 100%; 13 | max-width: 100%; 14 | } 15 | 16 | body { 17 | /*background-color: #f6f6f6;*/ 18 | background-color: #ECEFF7; 19 | } 20 | 21 | .reponame { 22 | width: 100%; 23 | max-width: 100%; 24 | padding: 10px; 25 | display: flex; 26 | flex-direction: row; 27 | justify-content: flex-start; 28 | align-items: center; 29 | background-color: #F9F9F9; 30 | margin-bottom: 20px; 31 | } 32 | 33 | .reponame .repo { 34 | /*width: 20%;*/ 35 | flex: 1; 36 | font-weight: bold; /* 加粗 */ 37 | font-size: 16px; /* 字体大小 */ 38 | /*font-family: Arial, sans-serif; !* 字体类型 *!*/ 39 | } 40 | 41 | .reponame .repo-item { 42 | flex: 9; 43 | text-align: left; 44 | /*width: 80%;*/ 45 | margin-bottom: 0 !important; 46 | margin-left: 10px; 47 | font-size: 16px; /* 字体大小 */ 48 | word-break: break-all;; 49 | /*font-family: Arial, sans-serif; !* 字体类型 *!*/ 50 | } 51 | 52 | /*#title {*/ 53 | /* margin-left: 10px; !* 调整间距 *!*/ 54 | /*}*/ 55 | 56 | .item { 57 | /*display: none;*/ 58 | } 59 | 60 | .container { 61 | /*position: relative;*/ 62 | /*min-height:100vh;*/ 63 | } 64 | 65 | .content { 66 | 67 | display: none; 68 | /*display: block;*/ 69 | padding: 20px; 70 | background-color: #f9f9f9; 71 | border-radius: 20px; 72 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 73 | /*max-width: 600px;*/ 74 | max-width: 90%; 75 | margin: 10px auto; 76 | /*border-radius: 10px;*/ 77 | } 78 | 79 | .item_title { 80 | font-weight: bold; 81 | font-size: 16px; 82 | border-bottom: 2px solid #3498db; /* 添加标题底部边框并使用深蓝色 */ 83 | padding-bottom: 10px; 84 | margin-bottom: 10px; 85 | } 86 | 87 | .item_content { 88 | /*min-height: 20vh;*/ 89 | padding: 10px; 90 | font-family: Arial, sans-serif; 91 | /*font-size: 1em;*/ 92 | border: 1px solid #e1e1e1; /* 使用浅灰色边框 */ 93 | border-radius: 5px; 94 | margin-bottom: 15px; 95 | background-color: #ffffff; /* 白色背景 */ 96 | white-space: pre-line; 97 | word-break: break-all; 98 | font-size: 14px; 99 | } 100 | /*, 101 | "content_scripts": [ 102 | { 103 | "js": [ 104 | "content-script.js" 105 | ], 106 | "matches": [ 107 | "https://github.com/*" 108 | ], 109 | "injection": [ 110 | "css/Content_Button.css", 111 | "bootstrap/css/bootstrap.min.css", 112 | "bootstrap/js/bootstrap.min.js" 113 | ], 114 | "run_at": "document_end", 115 | "all_frames": true 116 | } 117 | ]*/ 118 | 119 | .loader-container { 120 | 121 | position: absolute; 122 | top: 50%; 123 | left: 50%; 124 | transform: translate(-50%, -50%); 125 | /*display: flex;*/ 126 | /*flex-direction: column;*/ 127 | text-align: center; 128 | /*display: none;*/ 129 | /*align-items: center;*/ 130 | /*justify-content: center;*/ 131 | 132 | } 133 | 134 | .loader { 135 | display: block; 136 | border: 5px solid white; /* Light grey */ 137 | border-top: 3px solid #3498db; /* Blue */ 138 | border-radius: 50%; 139 | width: 50px; 140 | height: 50px; 141 | animation: spin 2s linear infinite; 142 | /*margin-left: 15%;*/ 143 | margin: 0 auto; 144 | 145 | } 146 | 147 | #loadcontent { 148 | padding-top: 20px; 149 | /*margin-left: 1px;*/ 150 | /*padding-left: 0.2em;*/ 151 | } 152 | 153 | @keyframes spin { 154 | 0% { 155 | transform: rotate(0deg); 156 | } 157 | 100% { 158 | transform: rotate(360deg); 159 | } 160 | } 161 | 162 | .footer { 163 | display: flex; 164 | text-align: center; 165 | flex-direction: row; 166 | justify-content: center; 167 | align-items: center; 168 | /*width: 100%!important;*/ 169 | background-color: #f9f9f9; 170 | padding: 10px; 171 | /*text-align: center;*/ 172 | /*position: absolute;*/ 173 | /*bottom: 0;*/ 174 | width: 100%; 175 | 176 | 177 | 178 | } 179 | 180 | .footerimg { 181 | /*margin-top: 3px;*/ 182 | width: 25px; 183 | height: 25px; 184 | border-radius: 9px; 185 | margin-right: 7px; 186 | } 187 | 188 | .footer span { 189 | margin-right: 7px; 190 | /*font-size: 25px;*/ 191 | } 192 | 193 | /* 194 | 195 | 196 | 197 | */ -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/images/icon-128.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/images/icon-16.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/images/icon-48.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/images/logo.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "AI code analyzer", 4 | "description": "AI analyzes code with KuaFuAI to help you understand its purpose, language, framework,module, and code structure quickly.", 5 | "version": "0.0.2", 6 | "icons": { 7 | "16": "images/icon-16.png", 8 | "48": "images/icon-48.png", 9 | "128": "images/icon-128.png" 10 | }, 11 | "side_panel": { 12 | "default_path": "sidepanel.html" 13 | }, 14 | "permissions": [ 15 | "sidePanel", 16 | "tabs", 17 | "scripting" 18 | ], 19 | "homepage_url": "https://www.kuafuai.net", 20 | "background": { 21 | "service_worker": "service-worker.js" 22 | }, 23 | "host_permissions": [ 24 | "https://github.com/*" 25 | ], 26 | "action": { 27 | "default_title": "KuaFuAI" 28 | } 29 | } -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140756357.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140756357.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140947012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140947012.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140949410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108140949410.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108141054506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108141054506.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108141159271.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108141159271.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143057951.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143057951.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143242430.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143242430.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143405016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143405016.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143419728.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143419728.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143638495.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143638495.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143755396.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108143755396.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108491651651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.2/readme.assets/image-20231108491651651.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/readme.md: -------------------------------------------------------------------------------- 1 | # AI code analyzer 2 | 3 | ## 用途介绍 4 | 5 | **AI code analyzer** 插件旨在通过**一键式操作**,为不同水平的开发者提供专业、全面且直观的代码分析,无论用户处于学习曲线的哪个点,都能有效提升他们的工作效率和代码理解, 6 | 7 | 以本项目为例,可以看到仓库名称后面的 AI Analysis ,点击后,短短几秒,插件便展开了全面分析,它精准捕获了 DevOpsGPT 项目的语言精髓、框架构建、使用的模块、服务用途,乃至代码结构的全貌。 8 | 9 | ![](readme.assets/image-20231108491651651.png) 10 | 11 | 同时本插件具备以下优势: 12 | 13 | - **多语言分析:**从 Python 到 Java ,再到 JavaScript ,支持多种语言一键分析。 14 | - **框架识别:**是 React 还是 Vue?Springboot 还是 Flask ,AI Analysis 带您秒懂项目背后的框架选择。 15 | - **模块用途:**每个库和模块,它都能为您描绘清晰的功能地图。 16 | - **实时反馈:**在浏览 GitHub 时,我们的AI助手提供即时的代码分析,帮助你辨识最佳实践和常见模式,就像有一位私人导师在旁指导。 17 | - **连接概念与实际应用:**通过分析真实世界的项目,你可以看到理论如何转化为实践,加深对编程语言和框架的理解。 18 | - **深度代码分析:**即使是最复杂的项目,我们的插件也能提供深入的代码结构和框架分析,帮助你快速定位到重要的部分。 19 | 20 | ## 安装方式 21 | 22 | ### 扩展市场安装 23 | 24 | - 点击Chrome浏览器右上角图标,扩展程序,访问Chrome应用商店 25 | 26 | ![image-20231108143057951](readme.assets/image-20231108143057951.png) 27 | 28 | - 搜索框中输入“AI code analyzer”,点击搜索 29 | 30 | ![image-20231108143242430](readme.assets/image-20231108143242430.png) 31 | 32 | - 点击“更多扩展程序” 33 | 34 | ![image-20231108143419728](readme.assets/image-20231108143419728.png) 35 | 36 | - 找到如下列表项,点击进入详情页 37 | 38 | ![image-20231108143638495](readme.assets/image-20231108143638495.png) 39 | 40 | - 点击“添加到Chrome”,即可完成安装 41 | 42 | ![image-20231108143755396](readme.assets/image-20231108143755396.png) 43 | 44 | ### 源码安装 45 | 46 | - 点击Chrome浏览器右上角图标,扩展程序,管理扩展程序 47 | 48 | ![image-20231108140756357](readme.assets/image-20231108140756357.png) 49 | 50 | - 打开“开发者模式” 51 | 52 | ![image-20231108140949410](readme.assets/image-20231108140949410.png) 53 | 54 | - 点击“加载已解压的扩展程序” 55 | 56 | ![image-20231108141054506](readme.assets/image-20231108141054506.png) 57 | 58 | - 选择源码根目录,点击确认,即可完成插件的安装 59 | 60 | ![image-20231108141159271](readme.assets/image-20231108141159271.png) -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.2/sidepanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

KuaFuAI

19 |

AI analyzes code with KuaFuAI to help you quickly learn code purpose, code language and 20 | development 21 | framework, and even detailed code structure information.

22 |
23 |

You can go to the github repository homepage and click the "AI Analyze" button, the analysis result will be 24 | displayed in the sidebar, and the analysis time is expected to be 5-10 minutes

25 | Go to the 26 | official website 27 |
28 |
29 | 30 |
31 | 32 | 33 | 34 |
35 |
36 |
Repo:
37 | 38 |
39 | 40 | 55 | 56 |
57 | 58 | 59 |
60 |
61 |
62 |
63 |
Loading....
64 |
65 |
66 | 67 | 68 |
69 |
70 |
Language
71 |

72 |
73 |
74 |
Framework
75 |

76 |
77 | 78 |
79 |
Module
80 |

81 |
82 |
83 |
Service usage
84 |

85 |
86 |
87 |
Code structure
88 |

89 |

90 |
91 |
92 | 93 |
94 | 95 |
96 |
97 | 98 | 99 | Powered By 100 | KuaFuAI 101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/bootstrap/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.6.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2022 The Bootstrap Authors 4 | * Copyright 2011-2022 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/css/sidepanel.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | 7 | #sidepanel{ 8 | display: none; 9 | } 10 | 11 | .all { 12 | width: 100%; 13 | max-width: 100%; 14 | } 15 | 16 | body { 17 | /*background-color: #f6f6f6;*/ 18 | background-color: #ECEFF7; 19 | } 20 | 21 | .reponame { 22 | width: 100%; 23 | max-width: 100%; 24 | padding: 10px; 25 | display: flex; 26 | flex-direction: row; 27 | justify-content: flex-start; 28 | align-items: center; 29 | background-color: #F9F9F9; 30 | margin-bottom: 20px; 31 | } 32 | 33 | .reponame .repo { 34 | /*width: 20%;*/ 35 | flex: 1; 36 | font-weight: bold; /* 加粗 */ 37 | font-size: 16px; /* 字体大小 */ 38 | /*font-family: Arial, sans-serif; !* 字体类型 *!*/ 39 | } 40 | 41 | .reponame .repo-item { 42 | flex: 9; 43 | text-align: left; 44 | /*width: 80%;*/ 45 | margin-bottom: 0 !important; 46 | margin-left: 10px; 47 | font-size: 16px; /* 字体大小 */ 48 | word-break: break-all;; 49 | /*font-family: Arial, sans-serif; !* 字体类型 *!*/ 50 | } 51 | 52 | /*#title {*/ 53 | /* margin-left: 10px; !* 调整间距 *!*/ 54 | /*}*/ 55 | 56 | .item { 57 | /*display: none;*/ 58 | } 59 | 60 | .container { 61 | /*position: relative;*/ 62 | /*min-height:100vh;*/ 63 | } 64 | 65 | .content { 66 | 67 | display: none; 68 | /*display: block;*/ 69 | padding: 20px; 70 | background-color: #f9f9f9; 71 | border-radius: 20px; 72 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 73 | /*max-width: 600px;*/ 74 | max-width: 90%; 75 | margin: 10px auto; 76 | /*border-radius: 10px;*/ 77 | } 78 | 79 | .item_title { 80 | font-weight: bold; 81 | font-size: 16px; 82 | border-bottom: 2px solid #3498db; /* 添加标题底部边框并使用深蓝色 */ 83 | padding-bottom: 10px; 84 | margin-bottom: 10px; 85 | } 86 | 87 | .item_content { 88 | /*min-height: 20vh;*/ 89 | padding: 10px; 90 | font-family: Arial, sans-serif; 91 | /*font-size: 1em;*/ 92 | border: 1px solid #e1e1e1; /* 使用浅灰色边框 */ 93 | border-radius: 5px; 94 | margin-bottom: 15px; 95 | background-color: #ffffff; /* 白色背景 */ 96 | white-space: pre-line; 97 | word-break: break-all; 98 | font-size: 14px; 99 | } 100 | /*, 101 | "content_scripts": [ 102 | { 103 | "js": [ 104 | "content-script.js" 105 | ], 106 | "matches": [ 107 | "https://github.com/*" 108 | ], 109 | "injection": [ 110 | "css/Content_Button.css", 111 | "bootstrap/css/bootstrap.min.css", 112 | "bootstrap/js/bootstrap.min.js" 113 | ], 114 | "run_at": "document_end", 115 | "all_frames": true 116 | } 117 | ]*/ 118 | 119 | .loader-container { 120 | 121 | position: absolute; 122 | top: 50%; 123 | left: 50%; 124 | transform: translate(-50%, -50%); 125 | /*display: flex;*/ 126 | /*flex-direction: column;*/ 127 | text-align: center; 128 | /*display: none;*/ 129 | /*align-items: center;*/ 130 | /*justify-content: center;*/ 131 | 132 | } 133 | 134 | .loader { 135 | display: block; 136 | border: 5px solid white; /* Light grey */ 137 | border-top: 3px solid #3498db; /* Blue */ 138 | border-radius: 50%; 139 | width: 50px; 140 | height: 50px; 141 | animation: spin 2s linear infinite; 142 | /*margin-left: 15%;*/ 143 | margin: 0 auto; 144 | 145 | } 146 | 147 | #loadcontent { 148 | padding-top: 20px; 149 | /*margin-left: 1px;*/ 150 | /*padding-left: 0.2em;*/ 151 | } 152 | 153 | @keyframes spin { 154 | 0% { 155 | transform: rotate(0deg); 156 | } 157 | 100% { 158 | transform: rotate(360deg); 159 | } 160 | } 161 | 162 | .footer { 163 | display: flex; 164 | text-align: center; 165 | flex-direction: row; 166 | justify-content: center; 167 | align-items: center; 168 | /*width: 100%!important;*/ 169 | background-color: #f9f9f9; 170 | padding: 10px; 171 | /*text-align: center;*/ 172 | /*position: absolute;*/ 173 | /*bottom: 0;*/ 174 | width: 100%; 175 | 176 | 177 | 178 | } 179 | 180 | .footerimg { 181 | /*margin-top: 3px;*/ 182 | width: 25px; 183 | height: 25px; 184 | border-radius: 9px; 185 | margin-right: 7px; 186 | } 187 | 188 | .footer span { 189 | margin-right: 7px; 190 | /*font-size: 25px;*/ 191 | } 192 | 193 | -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.3/images/icon-128.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.3/images/icon-16.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.3/images/icon-48.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuafuai/DevOpsGPT/8b5e1b484e0e1108b6f5f6511294d3067e420bc9/templete/ai_code_analyzer_0.0.3/images/logo.png -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/lock.js: -------------------------------------------------------------------------------- 1 | let lock = undefined; -------------------------------------------------------------------------------- /templete/ai_code_analyzer_0.0.3/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "AI code analyzer", 4 | "description": "AI analyzes code with KuaFuAI to help you understand its purpose, language, framework,module, and code structure quickly.", 5 | "version": "0.0.3", 6 | "icons": { 7 | "16": "images/icon-16.png", 8 | "48": "images/icon-48.png", 9 | "128": "images/icon-128.png" 10 | }, 11 | "permissions": [ 12 | "sidePanel", 13 | "tabs", 14 | "scripting", 15 | "storage" 16 | ], 17 | "homepage_url": "https://www.kuafuai.net", 18 | "background": { 19 | "service_worker": "service-worker.js" 20 | }, 21 | "content_scripts": [ 22 | { 23 | "matches": [ 24 | "https://*.github.com/*" 25 | ], 26 | "js": [ 27 | "lock.js" 28 | ] 29 | } 30 | ], 31 | "host_permissions": [ 32 | "https://github.com/*" 33 | ], 34 | "action": { 35 | "default_title": "KuaFuAI" 36 | } 37 | } --------------------------------------------------------------------------------