├── .commitlintrc.yml ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── enhancement.yml │ ├── feature-request.yml │ ├── proposal.yml │ └── support-request.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── generate-changelogs.yml │ ├── pick-build.yml │ ├── pr-build.yml │ ├── prerelease.yml │ ├── publish-docker-image.yml │ ├── publish-gh-pages.yml │ ├── release.yml │ └── sync-upstream.yml ├── .gitignore ├── .gitmodules ├── .gitmodules.d.mk ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.yml ├── .prettierignore ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOGS.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── codegen.ts ├── docker-compose.yml ├── docs ├── build.md ├── commit-msg-guide.md ├── getting-started.md └── preview.webp ├── hack └── checkout.sh ├── index.html ├── install ├── daed.desktop ├── daed.service ├── friendly-filenames.json ├── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ └── 64x64.png ├── package_after_install.sh └── package_after_remove.sh ├── package.json ├── pnpm-lock.yaml ├── public └── logo.webp ├── publish.Dockerfile ├── src ├── App.tsx ├── Router.tsx ├── apis │ ├── index.ts │ ├── mutation.ts │ └── query.ts ├── components │ ├── ConfigFormModal.tsx │ ├── ConfigureNodeFormModal │ │ ├── HTTPForm.tsx │ │ ├── Hysteria2Form.tsx │ │ ├── JuicityForm.tsx │ │ ├── SSForm.tsx │ │ ├── SSRForm.tsx │ │ ├── Socks5Form.tsx │ │ ├── TrojanForm.tsx │ │ ├── TuicForm.tsx │ │ ├── V2rayForm.tsx │ │ └── index.tsx │ ├── DraggableResourceBadge.tsx │ ├── DraggableResourceCard.tsx │ ├── DroppableGroupCard.tsx │ ├── ExpandedTableRow.tsx │ ├── FormActions.tsx │ ├── GroupFormModal.tsx │ ├── Header.tsx │ ├── ImportResourceFormModal.tsx │ ├── PlainTextFormModal.tsx │ ├── QRCodeModal.tsx │ ├── RenameFormModal.tsx │ ├── Section.tsx │ ├── SelectItemWithDescription.tsx │ ├── SimpleCard.tsx │ ├── Table.tsx │ ├── UpdateSubscriptionAction.tsx │ └── index.ts ├── constants │ ├── default.ts │ ├── editor.ts │ ├── index.ts │ ├── misc.ts │ └── schema.ts ├── contexts │ └── index.tsx ├── i18n │ ├── index.ts │ └── locales │ │ ├── en.json │ │ └── zh-Hans.json ├── index.css ├── initialize.ts ├── main.tsx ├── pages │ ├── Experiment.tsx │ ├── MainLayout.tsx │ ├── Orchestrate │ │ ├── Config.tsx │ │ ├── DNS.tsx │ │ ├── Group.tsx │ │ ├── Node.tsx │ │ ├── Routing.tsx │ │ ├── Subscription.tsx │ │ └── index.tsx │ ├── Setup.tsx │ └── index.ts ├── schemas │ └── gql │ │ ├── fragment-masking.ts │ │ ├── gql.ts │ │ ├── graphql.ts │ │ └── index.ts ├── store │ └── index.ts ├── types │ └── index.d.ts ├── utils │ ├── dnd-kit.ts │ ├── helper.ts │ ├── index.test.ts │ ├── index.ts │ └── node.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.commitlintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - '@commitlint/config-conventional' 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Dockerfile 3 | docker-compose.yml 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | #editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/schemas -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | parser: '@typescript-eslint/parser' 3 | parserOptions: 4 | ecmaFeatures: 5 | jsx: true 6 | ecmaVersion: latest 7 | sourceType: module 8 | env: 9 | node: true 10 | plugins: 11 | - '@typescript-eslint' 12 | - react 13 | - import 14 | extends: 15 | - eslint:recommended 16 | - plugin:@typescript-eslint/eslint-recommended 17 | - plugin:@typescript-eslint/recommended 18 | - plugin:import/recommended 19 | - plugin:prettier/recommended 20 | - plugin:react-hooks/recommended 21 | - plugin:react/recommended 22 | - plugin:react/jsx-runtime 23 | - plugin:@tanstack/eslint-plugin-query/recommended 24 | settings: 25 | import/resolver: 26 | typescript: 27 | node: true 28 | react: 29 | version: detect 30 | rules: 31 | '@typescript-eslint/no-non-null-assertion': off 32 | padding-line-between-statements: 33 | - error 34 | - blankLine: always 35 | prev: '*' 36 | next: return 37 | - blankLine: always 38 | prev: '*' 39 | next: if 40 | - blankLine: always 41 | prev: 'if' 42 | next: '*' 43 | - blankLine: always 44 | prev: '*' 45 | next: switch 46 | - blankLine: always 47 | prev: switch 48 | next: '*' 49 | react/prop-types: off 50 | react/display-name: off 51 | react/self-closing-comp: error 52 | import/newline-after-import: error 53 | import/order: 54 | - error 55 | - newlines-between: always 56 | groups: 57 | - builtin 58 | - type 59 | - external 60 | - internal 61 | - sibling 62 | - index 63 | - parent 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: report any bugs 3 | title: '[Bug Report] ' 4 | labels: [topic/bug] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Checks 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - label: I have read the documentation 14 | required: true 15 | - label: Is it your first time sumbitting an issue 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: Current Behavior 21 | description: A concise description of what you're experiencing. 22 | validations: 23 | required: false 24 | 25 | - type: textarea 26 | attributes: 27 | label: Expected Behavior 28 | description: A concise description of what you expected to happen. 29 | validations: 30 | required: false 31 | 32 | - type: textarea 33 | attributes: 34 | label: Steps to Reproduce 35 | description: Steps to reproduce it (as minimally and precisely as possible). 36 | placeholder: | 37 | 1. In this environment... 38 | 2. With this config... 39 | 3. Do ... 40 | 4. See error... 41 | validations: 42 | required: false 43 | 44 | - type: textarea 45 | attributes: 46 | label: Environment 47 | description: | 48 | examples: 49 | - **Daed version**: v0.2.4 50 | - **OS (e.g `cat /etc/os-release`)**: Debian 12 51 | - **Kernel (e.g. `uname -a`)**: 6.4.8-arch1-1 52 | - **Others**: NA 53 | value: | 54 | - **Daed version**: 55 | - **OS (e.g `cat /etc/os-release`)**: 56 | - **Kernel (e.g. `uname -a`)**: 57 | - **Others**: 58 | validations: 59 | required: false 60 | 61 | - type: textarea 62 | attributes: 63 | label: Anything else? 64 | description: | 65 | Config? Links? References? Anything that will give us more context about the issue you are encountering! 66 | 67 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 68 | validations: 69 | required: false 70 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: '📚 Support/Q&A' 4 | url: https://github.com/daeuniverse/daed/discussions/categories/q-a-support 5 | about: >- 6 | Some questions are already answered in our github discussions Q&A section If you don't find an answer, [create a support ticket](https://github.com/daeuniverse/dae/discussions/new?category=q-a-support) 7 | 8 | - name: '💬 dae Documentation Link (en)' 9 | url: https://github.com/daeuniverse/dae/tree/main/docs/en 10 | about: 'daeuniverse Documentation Link' 11 | 12 | - name: '💬 dae Documentation Link (zh)' 13 | url: https://github.com/daeuniverse/dae/tree/main/docs/zh 14 | about: 'daeuniverse Documentation Link' 15 | 16 | - name: '💬 daed Documentation Link' 17 | url: https://github.com/daeuniverse/daed/blob/main/docs/getting-started.md 18 | about: 'daeuniverse Documentation Link' 19 | 20 | - name: '💬 daeuniverse Telegram Support' 21 | url: https://t.me/daeuniverse 22 | about: 'daeuniverse Telegram Support Channel' 23 | 24 | - name: '💬 daeuniverse GitHub Discussion Support' 25 | url: https://github.com/daeuniverse/dae/discussions 26 | about: 'daeuniverse GitHub Discussion Portal' 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.yml: -------------------------------------------------------------------------------- 1 | name: Enhancement 2 | description: suggest an enhancement to the daed project 3 | title: '[Enhancement] <title>' 4 | labels: [topic/enhancement] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Improvement Suggestion 9 | description: What would you like us to improve. 10 | validations: 11 | required: false 12 | 13 | - type: textarea 14 | attributes: 15 | label: Potential Benefits 16 | description: Why is this needed. 17 | validations: 18 | required: false 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: feature request related to daed 3 | title: '[Feature Request] <title>' 4 | labels: ['topic/feature'] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Greetings 9 | description: general greetings 10 | placeholder: | 11 | Thanks for supporting the @daeuniverse community! If you have a good idea that wishes us to integrate to the current daed project, feel free to elaborate here. 12 | 13 | You may also post your idea on the [Discussions](https://github.com/daeuniverse/daed/discussions) or the daeuniverse Telegram channel (https://t.me/daeuniverse) 14 | 15 | Prior to opening a feature request, please search for existing requests. 16 | 17 | If you find an existing feature that matches your needs, use the 👍 emoji to show your support for it. If the specifics of your use case are not covered in the existing feature request but the idea seems similar enough, please take the time to *add new conversation* which helps the feature's design evolve. 18 | 19 | If you do not find any other existing requests for the feature you desire, you should open a new feature request. Please take the time to help us understand your use-case as precisely as possible. Be sure to demonstrate that you've evaluated existing features and found them unsuitable. 20 | validations: 21 | required: false 22 | 23 | - type: textarea 24 | attributes: 25 | label: Feature Request 26 | description: What feature you would like us to integrate into the daed project. 27 | validations: 28 | required: false 29 | 30 | - type: textarea 31 | attributes: 32 | label: Use Cases 33 | description: Share with us what use cases the proposed new feature is categorized to. 34 | validations: 35 | required: false 36 | 37 | - type: textarea 38 | attributes: 39 | label: Potential Benefits 40 | description: Why is this needed. 41 | validations: 42 | required: false 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/proposal.yml: -------------------------------------------------------------------------------- 1 | name: Proposal 2 | description: new feature proposal (developer only) 3 | title: '[Proposal] <title>' 4 | labels: ['topic/proposal'] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Proposal 9 | description: What new feature you would like to integrate into the daed project. 10 | validations: 11 | required: false 12 | 13 | - type: textarea 14 | attributes: 15 | label: Use Cases 16 | description: What use cases the proposed new feature is categorized to. 17 | validations: 18 | required: false 19 | 20 | - type: textarea 21 | attributes: 22 | label: Potential Benefits 23 | description: Why is this needed. 24 | validations: 25 | required: false 26 | 27 | - type: textarea 28 | attributes: 29 | label: Scope 30 | description: What needs to be done. 31 | validations: 32 | required: false 33 | 34 | - type: textarea 35 | attributes: 36 | label: Reference 37 | description: Useful links 38 | validations: 39 | required: false 40 | 41 | - type: textarea 42 | attributes: 43 | label: Implementation 44 | description: PR links to this issue. 45 | validations: 46 | required: false 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.yml: -------------------------------------------------------------------------------- 1 | name: Support Request 2 | description: need some help from the community 3 | title: '[Support Request] <title>' 4 | labels: [topic/support] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Checks 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - label: I have read the documentation 14 | required: true 15 | - label: Is it your first time sumbitting an issue 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: Support Request 21 | description: What would you like us to support (In short summary). 22 | validations: 23 | required: false 24 | 25 | - type: textarea 26 | attributes: 27 | label: Current Behavior 28 | description: A concise description of what you're experiencing. 29 | validations: 30 | required: false 31 | 32 | - type: textarea 33 | attributes: 34 | label: Expected Behavior 35 | description: A concise description of what you expected to happen. 36 | validations: 37 | required: false 38 | 39 | - type: textarea 40 | attributes: 41 | label: Steps to Reproduce 42 | description: Steps to reproduce it (as minimally and precisely as possible). 43 | placeholder: | 44 | 1. In this environment... 45 | 2. With this config... 46 | 3. Do ... 47 | 4. See error... 48 | validations: 49 | required: false 50 | 51 | - type: textarea 52 | attributes: 53 | label: Environment 54 | description: | 55 | examples: 56 | - **Daed version**: v0.2.4 57 | - **OS (e.g `cat /etc/os-release`)**: Debian 12 58 | - **Kernel (e.g. `uname -a`)**: 6.4.8-arch1-1 59 | - **Others**: NA 60 | value: | 61 | - **Daed version**: 62 | - **OS (e.g `cat /etc/os-release`)**: 63 | - **Kernel (e.g. `uname -a`)**: 64 | - **Others**: 65 | validations: 66 | required: false 67 | 68 | - type: textarea 69 | attributes: 70 | label: Anything else? 71 | description: | 72 | Config? Links? References? Anything that will give us more context about the issue you are encountering! 73 | 74 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 75 | validations: 76 | required: false 77 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | <!-- NOTE: Please read the CONTRIBUTING.md guidelines before submitting your patch, and ensure you followed them all: https://github.com/daeuniverse/dae/blob/master/CONTRIBUTING.md --> 2 | 3 | ### Background 4 | 5 | <!--- Why is this change required? What problem does it solve? --> 6 | 7 | ### Checklist 8 | 9 | - [ ] The Pull Request has been fully tested 10 | - [ ] There's an entry in the CHANGELOGS 11 | - [ ] There is a user-facing docs PR against https://github.com/daeuniverse/daed 12 | 13 | ### Full Changelogs 14 | 15 | - [Implement ...] 16 | 17 | ### Issue Reference 18 | 19 | <!--- If it fixes an open issue, please link to the issue here. --> 20 | 21 | Closes #_[issue number]_ 22 | 23 | ### Test Result 24 | 25 | <!--- Attach test result here. --> 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.github/workflows/generate-changelogs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Changelogs 2 | run-name: 'chore(release): generate changelogs for ${{ inputs.previous_release_tag }}..${{ inputs.future_release_tag }}' 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | previous_release_tag: 8 | required: true 9 | description: previous release tag 10 | future_release_tag: 11 | required: true 12 | description: future release tag 13 | dry_run: 14 | required: true 15 | description: dry run 16 | default: true 17 | 18 | jobs: 19 | build: 20 | name: Generate changelogs 21 | runs-on: ubuntu-latest 22 | permissions: 23 | issues: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Generate release changelogs 28 | uses: daeuniverse/changelogs-generator-action@main 29 | id: changelog 30 | with: 31 | # https://github.com/daeuniverse/changelogs-generator-action 32 | previousRelease: ${{ inputs.previous_release_tag }} 33 | futureRelease: ${{ inputs.future_release_tag }} 34 | token: ${{ secrets.GH_TOKEN }} 35 | 36 | - name: Print outputs 37 | shell: bash 38 | run: | 39 | echo "${{ steps.changelog.outputs.changelogs }}" 40 | 41 | - name: Create an issue with proposed changelogs 42 | if: ${{ inputs.dry_run == 'false' }} 43 | uses: dacbd/create-issue-action@main 44 | with: 45 | token: ${{ secrets.GH_TOKEN }} 46 | title: '[Release Changelogs] ${{ inputs.future_release_tag }}' 47 | labels: automated-issue,release 48 | assignees: "yqlbu,mzz2017,kunish" 49 | body: | 50 | ${{ steps.changelog.outputs.changelogs }} 51 | -------------------------------------------------------------------------------- /.github/workflows/publish-docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker Image 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | release: 9 | types: [published] 10 | 11 | 12 | jobs: 13 | 14 | build-web: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: 'recursive' 20 | 21 | - uses: pnpm/action-setup@v3.0.0 22 | with: 23 | version: latest 24 | 25 | - uses: actions/setup-node@v4 26 | with: 27 | cache: pnpm 28 | node-version: latest 29 | 30 | - name: Build 31 | run: | 32 | pnpm install 33 | pnpm build 34 | 35 | - name: Upload artifact - web 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: web 39 | path: dist 40 | 41 | 42 | publish-docker-image: 43 | runs-on: ubuntu-latest 44 | needs: 45 | - build-web 46 | 47 | steps: 48 | - uses: actions/checkout@v4 49 | with: 50 | submodules: 'recursive' 51 | 52 | - name: Download artifact - web 53 | uses: actions/download-artifact@v4 54 | with: 55 | name: web 56 | path: dist/ 57 | 58 | - name: Prepare Tag 59 | # Borrowed from daeuniverse/dae 60 | id: prep 61 | env: 62 | REF: ${{ github.ref }} 63 | run: | 64 | DIR="$(pwd)/dist/" 65 | if [ -d "$DIR" ]; then 66 | ### Take action if $DIR exists ### 67 | echo "Installing config files in ${DIR}..." 68 | else 69 | ### Control will jump here if $DIR does NOT exists ### 70 | echo "Error: ${DIR} not found. Can not continue." 71 | exit 1 72 | fi 73 | if [[ "$REF" == "refs/tags/v"* ]]; then 74 | tag=$(git describe --tags $(git rev-list --tags --max-count=1)) 75 | tag=${tag:1} 76 | else 77 | tag=$(git log -1 --format="%cd" --date=short | sed s/-//g) 78 | fi 79 | echo "IMAGE=daeuniverse/daed" >> $GITHUB_OUTPUT 80 | echo "TAG=$tag" >> $GITHUB_OUTPUT 81 | 82 | - name: Set up QEMU 83 | uses: docker/setup-qemu-action@v3 84 | 85 | - name: Set up Docker Buildx 86 | uses: docker/setup-buildx-action@v3 87 | id: buildx 88 | 89 | - name: Login to Docker Hub 90 | uses: docker/login-action@v3 91 | with: 92 | username: ${{ secrets.DOCKERHUB_USERNAME }} 93 | password: ${{ secrets.DOCKERHUB_TOKEN }} 94 | 95 | - name: Login to ghrc.io 96 | uses: docker/login-action@v3 97 | with: 98 | registry: ghcr.io 99 | username: ${{ github.actor }} 100 | password: ${{ secrets.GITHUB_TOKEN }} 101 | 102 | - name: Login to quay.io 103 | uses: docker/login-action@v3 104 | with: 105 | registry: quay.io 106 | username: ${{ github.repository_owner }} 107 | password: ${{ secrets.QUAY_PASS }} 108 | 109 | - name: Build image 110 | if: github.event_name == 'release' 111 | uses: docker/build-push-action@v5 112 | with: 113 | context: . 114 | build-args: DAED_VERSION=${{ steps.prep.outputs.TAG }} 115 | builder: ${{ steps.buildx.outputs.name }} 116 | file: publish.Dockerfile 117 | target: prod 118 | platforms: linux/386,linux/amd64,linux/arm64,linux/arm/v7 119 | push: true 120 | tags: | 121 | ${{ github.repository }}:latest 122 | ${{ github.repository }}:${{ steps.prep.outputs.TAG }} 123 | ghcr.io/${{ github.repository }}:latest 124 | ghcr.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }} 125 | quay.io/${{ github.repository }}:latest 126 | quay.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }} 127 | cache-from: type=gha 128 | cache-to: type=gha,mode=max 129 | 130 | - name: Test Build 131 | if: github.event_name != 'release' 132 | uses: docker/build-push-action@v5 133 | with: 134 | context: . 135 | build-args: DAED_VERSION=${{ steps.prep.outputs.TAG }} 136 | builder: ${{ steps.buildx.outputs.name }} 137 | file: publish.Dockerfile 138 | target: prod 139 | platforms: linux/386,linux/amd64,linux/arm64,linux/arm/v7 140 | push: true 141 | tags: | 142 | ${{ github.repository }}:test 143 | ${{ github.repository }}:${{ steps.prep.outputs.TAG }}-test 144 | ghcr.io/${{ github.repository }}:test 145 | ghcr.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }}-test 146 | quay.io/${{ github.repository }}:test 147 | quay.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }}-test 148 | cache-from: type=gha 149 | cache-to: type=gha,mode=max 150 | -------------------------------------------------------------------------------- /.github/workflows/publish-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Publish Github Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish-gh-pages: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - uses: pnpm/action-setup@v3.0.0 15 | with: 16 | version: latest 17 | 18 | - uses: actions/setup-node@v4 19 | with: 20 | cache: pnpm 21 | node-version: latest 22 | 23 | - name: build 24 | run: | 25 | pnpm install 26 | pnpm build 27 | 28 | - name: Publish Github Pages 29 | uses: peaceiris/actions-gh-pages@v3 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./dist 33 | -------------------------------------------------------------------------------- /.github/workflows/sync-upstream.yml: -------------------------------------------------------------------------------- 1 | # _ _ 2 | # __| | __ _ ___ __| | 3 | # / _` |/ _` |/ _ \/ _` | 4 | # | (_| | (_| | __/ (_| | 5 | # \__,_|\__,_|\___|\__,_| 6 | # 7 | # Copyright (C) 2023 @daeuniverse <https://github.com/daeuniverse> 8 | # 9 | # This is a open-source software, liscensed under the MIT License. 10 | # See /License for more information. 11 | 12 | name: Synchronize Upstream 13 | 14 | on: 15 | workflow_dispatch: 16 | 17 | jobs: 18 | sync-dae-core: 19 | uses: daeuniverse/ci-seed-jobs/.github/workflows/sync-upstream.yml@master 20 | with: 21 | submodule-name: wing 22 | secrets: inherit 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # Make 25 | daed -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wing"] 2 | path = wing 3 | url = https://github.com/daeuniverse/dae-wing 4 | -------------------------------------------------------------------------------- /.gitmodules.d.mk: -------------------------------------------------------------------------------- 1 | submodule_ready=wing/.git 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm commitlint -e 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm test run 5 | pnpm lint-staged 6 | -------------------------------------------------------------------------------- /.lintstagedrc.yml: -------------------------------------------------------------------------------- 1 | 'package.json': 'sort-package-json' 2 | '*.{js,jsx,ts,tsx}': 'eslint --fix' 3 | '*.{js,jsx,ts,tsx,md,html,css,less,scss,json,yml,yaml,graphql}': 'prettier --write' 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "graphql.vscode-graphql", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode", 6 | "lokalise.i18n-ally" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[css]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[graphql]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "[html]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | }, 11 | "[javascript]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[json]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "[jsonc]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[typescript]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode" 22 | }, 23 | "[typescriptreact]": { 24 | "editor.defaultFormatter": "esbenp.prettier-vscode" 25 | }, 26 | "[yaml]": { 27 | "editor.defaultFormatter": "esbenp.prettier-vscode" 28 | }, 29 | "editor.codeActionsOnSave": { 30 | "source.fixAll": "explicit", 31 | "source.organizeImports": "explicit" 32 | }, 33 | "editor.formatOnSave": true, 34 | "i18n-ally.localesPaths": ["packages/i18n/locales"], 35 | "i18n-ally.keystyle": "nested" 36 | } 37 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @daeuniverse/governance 2 | /src/** @daeuniverse/daed 3 | /docs/** @daeuniverse/docs 4 | CHANGELOGS.md @daeuniverse/release 5 | /.github/** @daeuniverse/infra 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | If you want to contribute to a project and make it better, your help is very welcome. Contributing is also a great way to learn more about social coding on Github, new technologies and and their ecosystems and how to make constructive, helpful bug reports, feature requests and the noblest of all contributions: a good, clean pull request. 4 | 5 | ### Bug Reports and Feature Requests 6 | 7 | If you have found a `bug` or have a `feature request`, please use the search first in case a similar issue already exists. If not, please create an [issue](https://github.com/daeuniverse/dae-wing/issues/new) in this repository 8 | 9 | ### Code 10 | 11 | If you would like to fix a bug or implement a feature, please `fork` the repository and `create a Pull Request`. 12 | 13 | Before you start any Pull Request, `it is recommended that you create an issue` to discuss first if you have any doubts about requirement or implementation. That way you can be sure that the maintainer(s) agree on what to change and how, and you can hopefully get a quick merge afterwards. 14 | 15 | `Pull Requests` can only be merged once all status checks are green. 16 | 17 | ### How to make a clean pull request 18 | 19 | - Create a `personal fork` of the project on Github. 20 | - Clone the fork on your local machine. Your remote repo on Github is called `origin`. 21 | - Add the original repository as a remote called `upstream`. 22 | - If you created your fork a while ago be sure to pull upstream changes into your local repository. 23 | - Create a new branch to work on! Branch from `develop` if it exists, else from `master`. 24 | - Implement/fix your feature, comment your code. 25 | - Follow the code style of the project, including indentation. 26 | - If the project has tests run them! 27 | - Write or adapt tests as needed. 28 | - Add or change the documentation as needed. 29 | - Squash your commits into a single commit with git's [interactive rebase](https://help.github.com/articles/interactive-rebase). Create a new branch if necessary. 30 | - Push your branch to your fork on Github, the remote `origin`. 31 | - From your fork open a pull request in the correct branch. Target the project's `develop` branch if there is one, else go for `master`! 32 | - Once the pull request is approved and merged you can pull the changes from `upstream` to your local repo and delete 33 | your extra branch(es). 34 | 35 | And last but not least: Always write your commit messages in the present tense. Your commit message should describe what the commit, when applied, does to the code – not what you did to the code. 36 | 37 | ### Re-requesting a review 38 | 39 | Please do not ping your reviewer(s) by mentioning them in a new comment. Instead, use the re-request review functionality. Read more about this in the [ GitHub docs, Re-requesting a review ](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request#re-requesting-a-review). 40 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine as build-web 2 | 3 | WORKDIR /build 4 | 5 | COPY . . 6 | 7 | RUN corepack enable 8 | RUN corepack prepare pnpm@latest --activate 9 | RUN pnpm install 10 | RUN pnpm build 11 | 12 | 13 | 14 | FROM golang:1.21-bookworm as build-bundle 15 | 16 | RUN \ 17 | apt-get update; apt-get install -y git make llvm-15 clang-15; \ 18 | apt-get clean autoclean && apt-get autoremove -y && rm -rf /var/lib/{apt,dpkg,cache,log}/ 19 | 20 | # build bundle process 21 | ENV CGO_ENABLED=0 22 | ENV CLANG=clang-15 23 | ARG DAED_VERSION=self-build 24 | 25 | COPY --from=build-web /build/dist /build/web 26 | COPY --from=build-web /build/wing /build/wing 27 | 28 | WORKDIR /build/wing 29 | 30 | RUN make APPNAME=daed VERSION=$DAED_VERSION OUTPUT=daed WEB_DIST=/build/web/ bundle 31 | 32 | 33 | 34 | 35 | FROM alpine 36 | 37 | LABEL org.opencontainers.image.source=https://github.com/daeuniverse/daed 38 | 39 | RUN mkdir -p /usr/local/share/daed/ 40 | RUN mkdir -p /etc/daed/ 41 | RUN wget -O /usr/local/share/daed/geoip.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geoip.dat; \ 42 | wget -O /usr/local/share/daed/geosite.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geosite.dat 43 | COPY --from=build-bundle /build/wing/daed /usr/local/bin 44 | 45 | EXPOSE 2023 46 | 47 | CMD ["daed", "run", "-c", "/etc/daed"] 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2023 daeuniverse <dae@v2raya.org> 2 | 3 | Permission is hereby granted, free of 4 | charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OUTPUT ?= daed 2 | APPNAME ?= daed 3 | VERSION ?= 0.0.0.unknown 4 | 5 | .PHONY: submodules submodule 6 | 7 | daed: 8 | 9 | all: clean daed 10 | 11 | clean: 12 | rm -rf dist && rm -f daed 13 | 14 | ## Begin Git Submodules 15 | .gitmodules.d.mk: .gitmodules Makefile 16 | @set -e && \ 17 | submodules=$$(grep '\[submodule "' .gitmodules | cut -d'"' -f2 | tr '\n' ' ' | tr ' \n' '\n' | sed 's/$$/\/.git/g') && \ 18 | echo "submodule_ready=$${submodules}" > $@ 19 | 20 | -include .gitmodules.d.mk 21 | 22 | $(submodule_ready): .gitmodules.d.mk 23 | ifdef SKIP_SUBMODULES 24 | @echo "Skipping submodule update" 25 | else 26 | git submodule update --init --recursive -- "$$(dirname $@)" && \ 27 | touch $@ 28 | endif 29 | 30 | submodule submodules: $(submodule_ready) 31 | @if [ -z "$(submodule_ready)" ]; then \ 32 | rm -f .gitmodules.d.mk; \ 33 | echo "Failed to generate submodules list. Please try again."; \ 34 | exit 1; \ 35 | fi 36 | ## End Git Submodules 37 | 38 | ## Begin Web 39 | PFLAGS ?= 40 | ifeq (,$(wildcard ./.git)) 41 | PFLAGS += HUSKY=0 42 | endif 43 | dist: package.json pnpm-lock.yaml 44 | $(PFLAGS) pnpm i 45 | pnpm build 46 | ## End Web 47 | 48 | ## Begin Bundle 49 | DAE_WING_READY=wing/graphql/service/config/global/generated_resolver.go 50 | 51 | $(DAE_WING_READY): wing 52 | cd wing && \ 53 | $(MAKE) deps && \ 54 | cd .. && \ 55 | touch $@ 56 | 57 | daed: submodule $(DAE_WING_READY) dist 58 | cd wing && \ 59 | $(MAKE) OUTPUT=../$(OUTPUT) APPNAME=$(APPNAME) WEB_DIST=../dist VERSION=$(VERSION) bundle 60 | ## End Bundle 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <h1 align="center">daed</h1> 2 | <p align="center"> 3 | <img width="100" src="public/logo.webp" /> 4 | </p> 5 | <p align="center"> 6 | <em>A modern web dashboard for dae</em> 7 | </p> 8 | 9 | <p align="center"> 10 | <img src="https://github.com/daeuniverse/daed/actions/workflows/build.yml/badge.svg" alt="build-status" /> 11 | <img src="https://custom-icon-badges.herokuapp.com/github/license/daeuniverse/daed?logo=law&color=orange" alt="license" /> 12 | <img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdaeuniverse%2Fdaed&count_bg=%235C3DC8&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false" alt="hits" /> 13 | <img src="https://custom-icon-badges.herokuapp.com/github/v/release/daeuniverse/daed?logo=rocket" alt="version"> 14 | <img src="https://custom-icon-badges.herokuapp.com/github/issues-pr-closed/daeuniverse/daed?color=purple&logo=git-pull-request&logoColor=white" alt="pr/issue" /> 15 | <img src="https://custom-icon-badges.herokuapp.com/github/last-commit/daeuniverse/daed?logo=history&logoColor=white" alt="lastcommit" /> 16 | </p> 17 | 18 | ## Features 19 | 20 | - [x] Easy to use, with keyboard navigation / shortcuts builtin 21 | - [x] Beautiful and intuitive UI 22 | - [x] Light / Dark mode 23 | - [x] Mobile friendly 24 | 25 | ## Getting Started 26 | 27 | Please refer to [Quick Start Guide](./docs/getting-started.md) to start using `daed` right away! 28 | 29 | ## Contrubuting 30 | 31 | Feel free to open issues or submit your PR, any feedbacks or help are greatly appreciated. 32 | 33 | Special thanks go to all [contributors](https://github.com/daeuniverse/daed/graphs/contributors). If you would like to contribute, please see the [instructions](./CONTRIBUTING.md). Also, it is recommended following the [commit-msg-guide](./docs/commit-msg-guide.md). 34 | 35 | ## License 36 | 37 | Made with passion 🔥 by [@daeuniverse](https://github.com/daeuniverse) 38 | 39 | The project is dual licensed under the [GNU Affero General Public License v3.0 (dae-wing)](https://github.com/daeuniverse/dae-wing/blob/main/LICENSE) and the [MIT License (daed)](https://github.com/daeuniverse/daed/blob/main/LICENSE). 40 | 41 | ### Dependencies used in this project 42 | 43 | - [Graphql](https://graphql.org) 44 | - [React](https://reactjs.org) 45 | - [Mantine](https://mantine.dev) 46 | - [dnd kit](https://dndkit.com) 47 | -------------------------------------------------------------------------------- /codegen.ts: -------------------------------------------------------------------------------- 1 | import { CodegenConfig } from '@graphql-codegen/cli' 2 | 3 | // eslint-disable-next-line import/no-default-export 4 | export default { 5 | overwrite: true, 6 | schema: process.env.SCHEMA_PATH, 7 | documents: 'src/**/*', 8 | generates: { 9 | 'src/schemas/gql/': { 10 | preset: 'client', 11 | }, 12 | }, 13 | hooks: { afterOneFileWrite: ['prettier -w'] }, 14 | } satisfies CodegenConfig 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | daed: 4 | privileged: true 5 | network_mode: host 6 | pid: host 7 | build: 8 | context: . 9 | volumes: 10 | - /sys:/sys 11 | - /etc/daed:/etc/daed 12 | -------------------------------------------------------------------------------- /docs/build.md: -------------------------------------------------------------------------------- 1 | ## How to run and build this project manually 2 | 3 | ### Prerequisites 4 | 5 | - nodejs 6 | - golang 7 | - clang 8 | - make 9 | - pnpm 10 | 11 | ### Bootstrap 12 | 13 | The following command will bootstrap the stack (`daed`, `dae-wing`, and `dae`) altogether. 14 | 15 | ```bash 16 | make 17 | ``` 18 | 19 | ### Advanced use case (Dev ONLY) 20 | 21 | > **Warning**: If you do NOT plan to use custom `Graphql` schema, please ignore this part. 22 | 23 | > **Note**: By default, Graphql type definitions and api bindings are generated automatically on the fly. 24 | > However, if you would like to configure new `schema` for Graphql, use environment variable `SCHEMA_PATH` to specify your schema endpoint 25 | > It can be a `url` starts with http(s) pointing to graphql endpoint or a static graphql schema file 26 | > Optionally, append `-w` or `--watch` at the end of the command to watch upcoming changes 27 | 28 | ```bash 29 | # e.g. 30 | # SCHEMA_PATH=http(s)://example.com/graphql pnpm codegen 31 | # SCHEMA_PATH=http(s)://example.com/graphql.schema pnpm codegen 32 | 33 | SCHEMA_PATH=/path/to/SCHEMA_PATH pnpm codegen --watch 34 | ``` 35 | 36 | ### Spin up server locally 37 | 38 | ```bash 39 | sudo chmod +x ./daed 40 | sudo install -Dm755 daed /usr/bin/ 41 | sudo daed run 42 | 43 | # helper 44 | sudo daed [-h,--help] 45 | ``` 46 | 47 | If everything goes well, open your browser and navigate to `http://localhost:2023` 48 | 49 | Happy Hacking! 50 | -------------------------------------------------------------------------------- /docs/commit-msg-guide.md: -------------------------------------------------------------------------------- 1 | # Semantic Commit Messages 2 | 3 | ## The reasons for these conventions 4 | 5 | - automatic generating of the changelog 6 | - simple navigation through Git history (e.g. ignoring the style changes) 7 | 8 | See how a minor change to your commit message style can make you a better developer. 9 | 10 | ## Format 11 | 12 | ``` 13 | `<type>(<scope>): <subject>` 14 | 15 | `<scope>` is optional 16 | ``` 17 | 18 | ## Example 19 | 20 | ``` 21 | feat: add hat wobble 22 | ^--^ ^------------^ 23 | | | 24 | | +-> Summary in present tense. 25 | | 26 | +-------> Type: chore, docs, feat, fix, refactor, style, or test. 27 | ``` 28 | 29 | Example `<type>` values: 30 | 31 | - `feat`: (new feature for the user, not a new feature for build script) 32 | - `fix`: (bug fix for the user, not a fix to a build script) 33 | - `docs`: (changes to the documentation) 34 | - `style`: (formatting, missing semi colons, etc; no production code change) 35 | - `refactor`: (refactoring production code, eg. renaming a variable) 36 | - `test`: (adding missing tests, refactoring tests; no production code change) 37 | - `chore`: (updating grunt tasks etc; no production code change, e.g. dependencies upgrade) 38 | - `perf`: (perfomance improvement change, e.g. better concurrency performance) 39 | - `ci`: (updating CI configuration files and scripts e.g. `.gitHub/workflows/*.yml` ) 40 | 41 | Example `<Scope>` values: 42 | 43 | - `init` 44 | - `runner` 45 | - `watcher` 46 | - `config` 47 | - `web-server` 48 | - `proxy` 49 | 50 | The `<scope>` can be empty (e.g. if the change is a global or difficult to assign to a single component), in which case the parentheses are omitted. In smaller projects such as Karma plugins, the `<scope>` is empty. 51 | 52 | ## Message Subject (First Line) 53 | 54 | The first line cannot be longer than `72` characters and should be followed by a blank line. The type and scope should always be lowercase as shown below 55 | 56 | ## Message Body 57 | 58 | use as in the `<subject>`, use the imperative, present tense: "change" not "changed" nor "changes". Message body should include motivation for the change and contrasts with previous behavior. 59 | 60 | ## Message footer 61 | 62 | ### Referencing issues 63 | 64 | Closed issues should be listed on a separate line in the footer prefixed with "Closes" keyword as the following: 65 | 66 | ``` 67 | Closes #234 68 | ``` 69 | 70 | or in the case of multiple issues: 71 | 72 | ``` 73 | Closes #123, #245, #992 74 | ``` 75 | 76 | ## References 77 | 78 | - <https://www.conventionalcommits.org/> 79 | - <https://seesparkbox.com/foundry/semantic_commit_messages> 80 | - <http://karma-runner.github.io/1.0/dev/git-commit-msg.html> 81 | - <https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html> 82 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide 2 | 3 | > **Note** 4 | > `daed` (UI component) is bundled with [dae-wing](https://github.com/daeuniverse/dae-wing) (backend API server) and [dae](https://github.com/daeuniverse/dae) (core). 5 | 6 | ## How to run 7 | 8 | > **Note** 9 | > - NEVER LET YOUR COMPUTER SLEEP OR HIBIRNATE WHILE PROXY IS STILL ON! 10 | > - NEVER SWITCH NETWORK WHILE PROXY IS STILL ON! 11 | > - TURN IT OFF BEFORE YOU MOVE OR LEAVE YOUR COMPUTER! 12 | > - OR YOU WILL HAVE POSSIBILITY TO NEED A REBOOT TO RECOVER YOUR NETWORK CONNECTIVITY! 13 | 14 | ### Download pre-compiled binaries 15 | 16 | Releases are available in <https://github.com/daeuniverse/daed/releases> 17 | 18 | > **Note** 19 | > If you would like to get a taste of new features, there are `PR Builds` available. Most of the time, newly proposed changes will be included in `PRs` and will be exported as cross-platform executable binaries in builds (GitHub Action Workflow Build). Noted that newly introduced features are sometimes buggy, do it at your own risk. However, we still highly encourage you to check out our latest builds as it may help us further analyze features stability and resolve potential bugs accordingly. 20 | 21 | PR-builds are available in <https://github.com/daeuniverse/daed/actions/workflows/pr-build.yml> 22 | 23 | ### Spin up server locally 24 | 25 | ```bash 26 | sudo chmod +x ./daed 27 | sudo install -Dm755 daed /usr/bin/ 28 | sudo daed run 29 | 30 | # helper 31 | sudo daed [-h,--help] 32 | ``` 33 | 34 | ### Debian / Ubuntu 35 | 36 | Releases are available in <https://github.com/daeuniverse/daed/releases> or the following command gets the latest version of the precompiled installation package consistent with your current system architecture 37 | 38 | ``````shell 39 | # Download 40 | wget -P /tmp https://github.com/daeuniverse/daed/releases/latest/download/installer-daed-linux-$(arch).deb 41 | 42 | # install 43 | sudo dpkg -i /tmp/installer-daed-linux-$(arch).deb 44 | rm /tmp/installer-daed-linux-$(arch).deb 45 | 46 | # Start daed 47 | sudo systemctl start daed 48 | 49 | # enable daed start automatically 50 | sudo systemctl enable daed 51 | `````` 52 | 53 | ### Red Hat / Fedora 54 | 55 | #### Fedora Copr 56 | 57 | daed has been released on [Fedora Copr](https://copr.fedorainfracloud.org/coprs/zhullyb/v2rayA/package/daed). 58 | 59 | ```shell 60 | sudo dnf copr enable zhullyb/v2rayA 61 | sudo dnf install daed 62 | ``` 63 | 64 | #### RPM Installation 65 | 66 | Releases are available in <https://github.com/daeuniverse/daed/releases> or the following command gets the latest version of the precompiled installation package consistent with your current system architecture 67 | 68 | ``````shell 69 | # Download 70 | wget -P /tmp https://github.com/daeuniverse/daed/releases/latest/download/installer-daed-linux-$(arch).rpm 71 | 72 | # install 73 | sudo rpm -ivh /tmp/installer-daed-linux-$(arch).rpm 74 | rm /tmp/installer-daed-linux-$(arch).rpm 75 | 76 | # Start daed 77 | sudo systemctl start daed 78 | 79 | # enable daed start automatically 80 | sudo systemctl enable daed 81 | `````` 82 | 83 | ### openSUSE 84 | 85 | Releases are available in <https://github.com/daeuniverse/daed/releases> or the following command gets the latest version of the precompiled installation package consistent with your current system architecture 86 | 87 | ``````shell 88 | # Download 89 | wget -P /tmp https://github.com/daeuniverse/daed/releases/latest/download/installer-daed-linux-$(arch).rpm 90 | 91 | # install 92 | sudo zypper install /tmp/installer-daed-linux-$(arch).rpm 93 | rm /tmp/installer-daed-linux-$(arch).rpm 94 | 95 | # Start daed 96 | sudo systemctl start daed 97 | 98 | # enable daed start automatically 99 | sudo systemctl enable daed 100 | `````` 101 | 102 | ### Arch Linux 103 | 104 | Releases are available in <https://github.com/daeuniverse/daed/releases> or the following command installs the latest version of the precompiled installation package consistent with your current system architecture 105 | 106 | #### AUR 107 | 108 | ##### Latest Release (Optimized Binary for x86-64 v3 / AVX2) 109 | 110 | ``````shell 111 | [yay/paru] -S daed-avx2-bin 112 | `````` 113 | 114 | ##### Latest Release (General x86-64 or aarch64) 115 | 116 | ``````shell 117 | [yay/paru] -S daed 118 | `````` 119 | 120 | ##### Latest Git Version 121 | 122 | ``````shell 123 | [yay/paru] -S daed-git 124 | `````` 125 | 126 | #### archlinuxcn 127 | 128 | ##### Latest Release (Optimized Binary for x86-64 v3 / AVX2) 129 | 130 | ``````shell 131 | sudo pacman -S daed-avx2-bin 132 | `````` 133 | 134 | ##### Latest Release (General x86-64 or aarch64) 135 | 136 | ``````shell 137 | sudo pacman -S daed 138 | `````` 139 | 140 | ##### Latest Git Version 141 | 142 | ``````shell 143 | sudo pacman -S daed-git 144 | `````` 145 | 146 | ### Docker (Experimental) 147 | 148 | Pre-built Docker images are available in `ghcr.io/daeuniverse/daed`, `quay.io/daeuniverse/daed` and `daeuniverse/daed`. 149 | 150 | #### Take `ghcr.io` for example, the command below pulls and runs the latest image 151 | 152 | ```shell 153 | sudo docker run -d \ 154 | --privileged \ 155 | --network=host \ 156 | --pid=host \ 157 | --restart=unless-stopped \ 158 | -v /sys:/sys \ 159 | -v /etc/daed:/etc/daed \ 160 | --name=daed \ 161 | ghcr.io/daeuniverse/daed:latest 162 | ``` 163 | 164 | #### You may also build from source: 165 | 166 | ```shell 167 | # clone the repository 168 | git clone https://github.com/daeuniverse/daed --recursive 169 | 170 | # build the image 171 | docker build -t daed . 172 | 173 | # run the container 174 | sudo docker run -d \ 175 | --privileged \ 176 | --network=host \ 177 | --pid=host \ 178 | --restart=unless-stopped \ 179 | -v /sys:/sys \ 180 | -v /etc/daed:/etc/daed \ 181 | --name=daed \ 182 | daed 183 | ``` 184 | 185 | 186 | > **NOTE** 187 | > - Docker currently supports only i386(x86-32), amd64(x86-64), armv7 and arm64(armv8). (Alpha) 188 | > - Only amd64 is tested working as expected, but not fully tested yet. (Beta) 189 | > - For self build from source, only amd64 and arm64 will run. 190 | > - Please refer to https://github.com/daeuniverse/daed/discussions/291 and relevant PRs and Issues for details 191 | > - Volunteers are welcomed. 192 | 193 | 194 | ## Access Panel 195 | 196 | If everything goes well, open your browser and navigate to `http://localhost:2023` 197 | 198 | Happy Hacking! 199 | -------------------------------------------------------------------------------- /docs/preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/docs/preview.webp -------------------------------------------------------------------------------- /hack/checkout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ask_continue() { 4 | read -r -p "All your code changes will be lost, continue? [y/N] " response 5 | case "$response" in 6 | [yY][eE][sS]|[yY]) 7 | echo $response 8 | ;; 9 | *) 10 | exit 0 11 | ;; 12 | esac 13 | } 14 | 15 | repo_rinse() { 16 | git reset --hard 17 | git pull 18 | git submodule foreach --recursive git reset --hard 19 | git submodule update --init --recursive 20 | } 21 | 22 | checkout_wing() { 23 | branch="$1" 24 | ask_continue 25 | repo_rinse 26 | cd wing && git fetch 27 | result=$(git branch -l -r "*/$branch") 28 | if [[ "$result" == "" ]]; then 29 | echo "No such branch: $branch" 30 | exit 1 31 | fi 32 | git checkout "$branch" 33 | git pull 34 | git submodule update --recursive 35 | go mod tidy 36 | } 37 | 38 | checkout_core() { 39 | branch="$1" 40 | ask_continue 41 | repo_rinse 42 | cd wing/dae-core && git fetch 43 | result=$(git branch -l -r "*/$branch") 44 | if [[ "$result" == "" ]]; then 45 | echo "No such branch: $branch" 46 | exit 1 47 | fi 48 | git checkout "$branch" 49 | git pull 50 | git submodule update --recursive 51 | cd .. 52 | go mod tidy 53 | } 54 | 55 | show_helps() { 56 | echo -e "\033[1;4mUsage:\033[0m" 57 | echo " $0 [command]" 58 | echo ' ' 59 | echo -e "\033[1;4mAvailable commands:\033[0m" 60 | echo " core <branch> checkout dae-core to given branch" 61 | echo " wing <branch> checkout dae-wing to given branch" 62 | echo " help show this help message" 63 | } 64 | 65 | # Main 66 | set -e 67 | current_dir=$(pwd) 68 | while [ $# != 0 ] ; do 69 | case "$1" in 70 | core) 71 | opt_core=1 72 | shift 73 | break 74 | ;; 75 | dae-core) 76 | opt_core=1 77 | shift 78 | break 79 | ;; 80 | wing) 81 | opt_wing=1 82 | shift 83 | break 84 | ;; 85 | dae-wing) 86 | opt_wing=1 87 | shift 88 | break 89 | ;; 90 | -h) 91 | opt_help=1 92 | shift 93 | break 94 | ;; 95 | *) 96 | opt_help=1 97 | echo "${RED}error: Unknown command: $1${RESET}" 98 | shift 99 | break 100 | ;; 101 | esac 102 | done 103 | if [ ! -z "$opt_help" ];then 104 | show_helps 105 | exit 1 106 | fi 107 | 108 | if [ ! -z "$opt_core" ];then 109 | checkout_core $1 110 | exit 0 111 | fi 112 | 113 | if [ ! -z "$opt_wing" ];then 114 | checkout_wing $1 115 | exit 0 116 | fi 117 | 118 | trap 'cd "$current_dir"' 0 1 2 3 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | <!doctype html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <link rel="icon" type="image/png" sizes="16x16" href="/logo.webp" /> 6 | <meta 7 | name="viewport" 8 | content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" 9 | /> 10 | <title>daed 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /install/daed.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Terminal=false 4 | Type=Application 5 | Name=daed Web Panel 6 | GenericName=A modern web dashboard for dae 7 | GenericName[zh_CN]=一款时尚的 dae 网络仪表盘 8 | Comment=A high-performance proxy tool based on eBPF 9 | Comment[zh_CN]=一款基于eBPF的高性能代理工具 10 | Categories=Network; 11 | Keywords=Internet;VPN;Proxy; 12 | Exec=xdg-open "http://127.0.0.1:2023" 13 | Icon=daed 14 | 15 | -------------------------------------------------------------------------------- /install/daed.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=daed is a integration solution of dae, API and UI. 3 | Documentation=https://github.com/daeuniverse/daed 4 | After=network-online.target docker.service systemd-sysctl.service 5 | Wants=network-online.target 6 | Conflicts=dae.service 7 | 8 | [Service] 9 | Type=simple 10 | User=root 11 | LimitNPROC=512 12 | LimitNOFILE=1048576 13 | ExecStart=/usr/bin/daed run -c /etc/daed/ 14 | Restart=on-abnormal 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /install/friendly-filenames.json: -------------------------------------------------------------------------------- 1 | { 2 | "linux-386": { "friendlyName": "linux-x86_32" }, 3 | "linux-amd64v1": { "friendlyName": "linux-x86_64" }, 4 | "linux-amd64v2": { "friendlyName": "linux-x86_64_v2_sse" }, 5 | "linux-amd64v3": { "friendlyName": "linux-x86_64_v3_avx2" }, 6 | "linux-amd64": { "friendlyName": "linux-x86_64" }, 7 | "linux-arm5": { "friendlyName": "linux-armv5" }, 8 | "linux-arm6": { "friendlyName": "linux-armv6" }, 9 | "linux-arm7": { "friendlyName": "linux-armv7" }, 10 | "linux-arm64": { "friendlyName": "linux-arm64" }, 11 | "linux-mips64le": { "friendlyName": "linux-mips64le" }, 12 | "linux-mips64": { "friendlyName": "linux-mips64" }, 13 | "linux-mipsle": { "friendlyName": "linux-mips32le" }, 14 | "linux-mips": { "friendlyName": "linux-mips32" }, 15 | "linux-riscv64": { "friendlyName": "linux-riscv64" } 16 | } 17 | -------------------------------------------------------------------------------- /install/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/1024x1024.png -------------------------------------------------------------------------------- /install/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/128x128.png -------------------------------------------------------------------------------- /install/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/16x16.png -------------------------------------------------------------------------------- /install/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/24x24.png -------------------------------------------------------------------------------- /install/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/256x256.png -------------------------------------------------------------------------------- /install/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/32x32.png -------------------------------------------------------------------------------- /install/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/48x48.png -------------------------------------------------------------------------------- /install/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/512x512.png -------------------------------------------------------------------------------- /install/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/install/icons/64x64.png -------------------------------------------------------------------------------- /install/package_after_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | systemctl daemon-reload 3 | 4 | if [ "$(systemctl is-active daed)" == 'active' ]; then 5 | systemctl restart daed.service 6 | echo "Restarting daed service, it might take a while." 7 | fi -------------------------------------------------------------------------------- /install/package_after_remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | systemctl daemon-reload 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "daed", 3 | "version": "v0.1.0", 4 | "private": true, 5 | "description": "A Web Dashboard For dae", 6 | "homepage": "https://daeuniverse.github.io/daed", 7 | "repository": "https://github.com/daeuniverse/daed", 8 | "license": "MIT", 9 | "author": { 10 | "name": "daeuniverse", 11 | "email": "dae@v2raya.org" 12 | }, 13 | "scripts": { 14 | "build": "vite build", 15 | "codegen": "graphql-codegen", 16 | "dev": "vite dev", 17 | "lint": "vite lint", 18 | "prepare": "husky install", 19 | "test": "vitest" 20 | }, 21 | "dependencies": { 22 | "@commitlint/cli": "^18.6.1", 23 | "@commitlint/config-conventional": "^18.6.3", 24 | "@dnd-kit/core": "^6.1.0", 25 | "@dnd-kit/modifiers": "^6.0.1", 26 | "@dnd-kit/sortable": "^7.0.2", 27 | "@dnd-kit/utilities": "^3.2.2", 28 | "@emotion/react": "^11.13.3", 29 | "@faker-js/faker": "^8.4.1", 30 | "@fontsource/fira-sans": "^5.1.0", 31 | "@fontsource/source-code-pro": "^5.1.0", 32 | "@graphiql/toolkit": "^0.9.2", 33 | "@graphql-codegen/cli": "5.0.0", 34 | "@graphql-codegen/client-preset": "4.1.0", 35 | "@graphql-codegen/introspection": "4.0.0", 36 | "@graphql-typed-document-node/core": "^3.2.0", 37 | "@mantine/carousel": "^6.0.22", 38 | "@mantine/core": "^6.0.22", 39 | "@mantine/dates": "^6.0.22", 40 | "@mantine/dropzone": "^6.0.22", 41 | "@mantine/form": "^7.13.5", 42 | "@mantine/hooks": "^6.0.22", 43 | "@mantine/modals": "^6.0.22", 44 | "@mantine/notifications": "^6.0.22", 45 | "@mantine/nprogress": "^6.0.22", 46 | "@mantine/prism": "^6.0.22", 47 | "@mantine/spotlight": "^6.0.22", 48 | "@mantine/tiptap": "^6.0.22", 49 | "@monaco-editor/react": "^4.6.0", 50 | "@nanostores/persistent": "^0.9.1", 51 | "@nanostores/react": "^0.7.3", 52 | "@parcel/watcher": "^2.5.0", 53 | "@tabler/icons-react": "^2.47.0", 54 | "@tanstack/eslint-plugin-query": "^5.60.1", 55 | "@tanstack/react-query": "^4.36.1", 56 | "@tanstack/react-query-devtools": "^4.36.1", 57 | "@tiptap/extension-link": "^2.9.1", 58 | "@tiptap/react": "^2.9.1", 59 | "@tiptap/starter-kit": "^2.9.1", 60 | "@types/node": "^20.17.6", 61 | "@types/react": "^18.3.12", 62 | "@types/react-copy-to-clipboard": "^5.0.7", 63 | "@types/react-dom": "^18.3.1", 64 | "@types/urijs": "^1.19.25", 65 | "@types/uuid": "^9.0.8", 66 | "@typescript-eslint/eslint-plugin": "^6.21.0", 67 | "@typescript-eslint/parser": "^6.21.0", 68 | "@vitejs/plugin-react-swc": "^3.7.1", 69 | "@vitest/ui": "^0.34.7", 70 | "dayjs": "^1.11.13", 71 | "embla-carousel-react": "8.0.0-rc14", 72 | "eslint": "^8.57.1", 73 | "eslint-config-prettier": "^9.1.0", 74 | "eslint-import-resolver-typescript": "^3.6.3", 75 | "eslint-plugin-import": "^2.31.0", 76 | "eslint-plugin-prettier": "5.0.1", 77 | "eslint-plugin-react": "^7.37.2", 78 | "eslint-plugin-react-hooks": "^4.6.2", 79 | "execa": "^8.0.1", 80 | "framer-motion": "^10.18.0", 81 | "graphiql": "^3.7.2", 82 | "graphql": "^16.9.0", 83 | "graphql-request": "^6.1.0", 84 | "husky": "^8.0.3", 85 | "i18next": "^23.16.5", 86 | "i18next-browser-languagedetector": "^7.2.1", 87 | "immer": "^10.1.1", 88 | "js-base64": "^3.7.7", 89 | "lint-staged": "^15.2.10", 90 | "mantine-datatable": "^6.0.8", 91 | "monaco-editor": "^0.44.0", 92 | "monaco-themes": "^0.4.4", 93 | "nanostores": "^0.9.5", 94 | "prettier": "^3.3.3", 95 | "qrcode.react": "^3.2.0", 96 | "react": "^18.3.1", 97 | "react-copy-to-clipboard": "^5.1.0", 98 | "react-dom": "^18.3.1", 99 | "react-i18next": "^13.5.0", 100 | "react-router": "^6.28.0", 101 | "react-router-dom": "^6.28.0", 102 | "simple-git": "^3.27.0", 103 | "sort-package-json": "^2.10.1", 104 | "typescript": "^5.6.3", 105 | "urijs": "^1.19.11", 106 | "vite": "^4.5.5", 107 | "vite-plugin-environment": "^1.1.3", 108 | "vitest": "^0.34.6", 109 | "zod": "^3.23.8" 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /public/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeuniverse/daed/c3588a904c932d1fc83ee51096761776003fc25c/public/logo.webp -------------------------------------------------------------------------------- /publish.Dockerfile: -------------------------------------------------------------------------------- 1 | # ATTENTION This part below is for publishing purpose only 2 | 3 | ARG DAED_VERSION 4 | 5 | FROM golang:1.22-bookworm as build 6 | 7 | RUN \ 8 | apt-get update; apt-get install -y git make llvm-15 clang-15; \ 9 | apt-get clean autoclean && apt-get autoremove -y && rm -rf /var/lib/{apt,dpkg,cache,log}/ 10 | 11 | # build bundle process 12 | ENV CGO_ENABLED=0 13 | ENV CLANG=clang-15 14 | ARG DAED_VERSION 15 | 16 | WORKDIR /build 17 | 18 | COPY ./dist/ ./web/ 19 | COPY ./wing/ ./wing/ 20 | 21 | WORKDIR /build/wing 22 | 23 | RUN make APPNAME=daed VERSION=$DAED_VERSION OUTPUT=daed WEB_DIST=/build/web/ bundle 24 | 25 | 26 | FROM alpine as prod 27 | 28 | LABEL org.opencontainers.image.source=https://github.com/daeuniverse/daed 29 | 30 | RUN mkdir -p /usr/local/share/daed/ 31 | RUN mkdir -p /etc/daed/ 32 | RUN wget -O /usr/local/share/daed/geoip.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geoip.dat; \ 33 | wget -O /usr/local/share/daed/geosite.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geosite.dat 34 | COPY --from=build /build/wing/daed /usr/local/bin 35 | 36 | EXPOSE 2023 37 | 38 | CMD ["daed", "run", "-c", "/etc/daed"] 39 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | ColorScheme, 4 | ColorSchemeProvider, 5 | createEmotionCache, 6 | MantineProvider, 7 | MantineThemeOverride, 8 | ScrollArea, 9 | } from '@mantine/core' 10 | import { useColorScheme } from '@mantine/hooks' 11 | import { ModalsProvider } from '@mantine/modals' 12 | import { Notifications } from '@mantine/notifications' 13 | import { useStore } from '@nanostores/react' 14 | import { useCallback, useEffect, useState } from 'react' 15 | 16 | import { QueryProvider } from '~/contexts' 17 | import { Router } from '~/Router' 18 | import { appStateAtom, colorSchemeAtom } from '~/store' 19 | 20 | const emotionCache = createEmotionCache({ key: 'mantine' }) 21 | 22 | export const App = () => { 23 | const appState = useStore(appStateAtom) 24 | const preferredColorScheme = useColorScheme() 25 | const [colorScheme, setColorScheme] = useState(preferredColorScheme) 26 | const toggleColorScheme = useCallback( 27 | (value?: ColorScheme) => { 28 | const toScheme = value || (colorScheme === 'dark' ? 'light' : 'dark') 29 | setColorScheme(toScheme) 30 | appStateAtom.setKey('preferredColorScheme', toScheme) 31 | }, 32 | [colorScheme], 33 | ) 34 | 35 | useEffect(() => { 36 | const darkModePreference = window.matchMedia('(prefers-color-scheme: dark)') 37 | const onDarkModeChange = (e: MediaQueryListEvent) => toggleColorScheme(e.matches ? 'dark' : 'light') 38 | 39 | darkModePreference.addEventListener('change', onDarkModeChange) 40 | 41 | return () => darkModePreference.removeEventListener('change', onDarkModeChange) 42 | }, [toggleColorScheme]) 43 | 44 | useEffect(() => { 45 | setColorScheme(appState.preferredColorScheme || preferredColorScheme) 46 | }, [setColorScheme, preferredColorScheme, appState.preferredColorScheme]) 47 | 48 | useEffect(() => { 49 | colorSchemeAtom.set(colorScheme) 50 | }, [colorScheme]) 51 | 52 | const themeObject: MantineThemeOverride = { 53 | colorScheme, 54 | fontFamily: 'Fira Sans, Monaco, Consolas, sans-serif', 55 | fontFamilyMonospace: 'Source Code Pro, Monaco, Consolas, monospace', 56 | primaryColor: 'violet', 57 | cursorType: 'pointer', 58 | components: { 59 | Stack: { defaultProps: { spacing: 'sm' } }, 60 | Group: { defaultProps: { spacing: 'sm' } }, 61 | Button: { defaultProps: { uppercase: true } }, 62 | ActionIcon: { defaultProps: { size: 'sm' } }, 63 | Tooltip: { defaultProps: { withArrow: true } }, 64 | HoverCard: { defaultProps: { withArrow: true } }, 65 | Modal: { 66 | defaultProps: { 67 | size: 'lg', 68 | radius: 'md', 69 | centered: true, 70 | scrollAreaComponent: ScrollArea.Autosize, 71 | }, 72 | }, 73 | ModalHeader: { 74 | defaultProps: (theme) => ({ 75 | bg: colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[4], 76 | }), 77 | }, 78 | ModalTitle: { 79 | defaultProps: { 80 | color: 'white', 81 | }, 82 | }, 83 | Drawer: { 84 | defaultProps: { 85 | size: 'lg', 86 | scrollAreaComponent: ScrollArea.Autosize, 87 | }, 88 | }, 89 | Menu: { 90 | styles: { 91 | label: { 92 | textTransform: 'uppercase', 93 | }, 94 | }, 95 | }, 96 | Select: { 97 | defaultProps: { 98 | withinPortal: true, 99 | size: 'xs', 100 | }, 101 | }, 102 | MultiSelect: { defaultProps: { size: 'xs' } }, 103 | Switch: { defaultProps: { size: 'xs' } }, 104 | Checkbox: { defaultProps: { size: 'xs' } }, 105 | Radio: { defaultProps: { size: 'xs' } }, 106 | RadioGroup: { defaultProps: { size: 'xs' } }, 107 | TextInput: { defaultProps: { size: 'xs' } }, 108 | NumberInput: { defaultProps: { size: 'xs' } }, 109 | }, 110 | } 111 | 112 | return ( 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ) 126 | } 127 | -------------------------------------------------------------------------------- /src/Router.tsx: -------------------------------------------------------------------------------- 1 | import { createGraphiQLFetcher } from '@graphiql/toolkit' 2 | import { useStore } from '@nanostores/react' 3 | import { GraphiQL } from 'graphiql' 4 | import { BrowserRouter, HashRouter, Route, Routes } from 'react-router-dom' 5 | 6 | import { ExperimentPage, MainLayout, OrchestratePage, SetupPage } from '~/pages' 7 | import { endpointURLAtom } from '~/store' 8 | 9 | export const Router = () => { 10 | const endpointURL = useStore(endpointURLAtom) 11 | const RouterType = import.meta.env.DEV ? BrowserRouter : HashRouter 12 | 13 | return ( 14 | 15 | 16 | }> 17 | } /> 18 | } /> 19 | 20 | 21 | } /> 22 | 23 | {endpointURL && ( 24 | 32 | } 33 | /> 34 | )} 35 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/apis/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mutation' 2 | export * from './query' 3 | -------------------------------------------------------------------------------- /src/components/ConfigureNodeFormModal/HTTPForm.tsx: -------------------------------------------------------------------------------- 1 | import { NumberInput, Select, TextInput } from '@mantine/core' 2 | import { useForm, zodResolver } from '@mantine/form' 3 | import { useTranslation } from 'react-i18next' 4 | import { z } from 'zod' 5 | 6 | import { FormActions } from '~/components/FormActions' 7 | import { DEFAULT_HTTP_FORM_VALUES, httpSchema } from '~/constants' 8 | import { GenerateURLParams, generateURL } from '~/utils' 9 | 10 | export const HTTPForm = ({ onLinkGeneration }: { onLinkGeneration: (link: string) => void }) => { 11 | const { t } = useTranslation() 12 | const { onSubmit, getInputProps, reset } = useForm & { protocol: 'http' | 'https' }>({ 13 | initialValues: { 14 | protocol: 'http', 15 | ...DEFAULT_HTTP_FORM_VALUES, 16 | }, 17 | validate: zodResolver(httpSchema), 18 | }) 19 | 20 | const handleSubmit = onSubmit((values) => { 21 | const generateURLParams: GenerateURLParams = { 22 | protocol: values.protocol, 23 | host: values.host, 24 | port: values.port, 25 | hash: values.name, 26 | } 27 | 28 | if (values.username && values.password) { 29 | Object.assign(generateURLParams, { 30 | username: values.username, 31 | password: values.password, 32 | }) 33 | } 34 | 35 | return onLinkGeneration(generateURL(generateURLParams)) 36 | }) 37 | 38 | return ( 39 |
40 | 50 | 51 | */} 52 | 53 | 54 | 55 | 56 | 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /src/components/ConfigureNodeFormModal/JuicityForm.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox, NumberInput, Select, TextInput } from '@mantine/core' 2 | import { useForm, zodResolver } from '@mantine/form' 3 | import { useTranslation } from 'react-i18next' 4 | import { z } from 'zod' 5 | 6 | import { FormActions } from '~/components/FormActions' 7 | import { DEFAULT_JUICITY_FORM_VALUES, juicitySchema } from '~/constants' 8 | import { generateURL } from '~/utils' 9 | 10 | export const JuicityForm = ({ onLinkGeneration }: { onLinkGeneration: (link: string) => void }) => { 11 | const { t } = useTranslation() 12 | const { onSubmit, getInputProps, reset } = useForm>({ 13 | initialValues: DEFAULT_JUICITY_FORM_VALUES, 14 | validate: zodResolver(juicitySchema), 15 | }) 16 | 17 | const handleSubmit = onSubmit((values) => { 18 | const query = { 19 | congestion_control: values.congestion_control, 20 | pinned_certchain_sha256: values.pinned_certchain_sha256, 21 | sni: values.sni, 22 | allow_insecure: values.allowInsecure, 23 | } 24 | 25 | return onLinkGeneration( 26 | generateURL({ 27 | protocol: 'juicity', 28 | username: values.uuid, 29 | password: values.password, 30 | host: values.server, 31 | port: values.port, 32 | hash: values.name, 33 | params: query, 34 | }), 35 | ) 36 | }) 37 | 38 | return ( 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 92 | 93 | 114 | ))} 115 | 116 | {values.plugin === 'simple-obfs' && ( 117 | 129 | )} 130 | 131 | {values.plugin === 'v2ray-plugin' && ( 132 | 70 | 71 | 102 | 103 | {values.obfs !== 'plain' && } 104 | 105 | 106 | 107 | ) 108 | } 109 | -------------------------------------------------------------------------------- /src/components/ConfigureNodeFormModal/Socks5Form.tsx: -------------------------------------------------------------------------------- 1 | import { NumberInput, TextInput } from '@mantine/core' 2 | import { useForm, zodResolver } from '@mantine/form' 3 | import { useTranslation } from 'react-i18next' 4 | import { z } from 'zod' 5 | 6 | import { FormActions } from '~/components/FormActions' 7 | import { DEFAULT_SOCKS5_FORM_VALUES, socks5Schema } from '~/constants' 8 | import { GenerateURLParams, generateURL } from '~/utils' 9 | 10 | export const Socks5Form = ({ onLinkGeneration }: { onLinkGeneration: (link: string) => void }) => { 11 | const { t } = useTranslation() 12 | const { onSubmit, getInputProps, reset } = useForm>({ 13 | initialValues: DEFAULT_SOCKS5_FORM_VALUES, 14 | validate: zodResolver(socks5Schema), 15 | }) 16 | 17 | const handleSubmit = onSubmit((values) => { 18 | const generateURLParams: GenerateURLParams = { 19 | protocol: 'socks5', 20 | host: values.host, 21 | port: values.port, 22 | hash: values.name, 23 | } 24 | 25 | if (values.username && values.password) { 26 | Object.assign(generateURLParams, { 27 | username: values.username, 28 | password: values.password, 29 | }) 30 | } 31 | 32 | return onLinkGeneration(generateURL(generateURLParams)) 33 | }) 34 | 35 | return ( 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/components/ConfigureNodeFormModal/TrojanForm.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox, NumberInput, Select, TextInput } from '@mantine/core' 2 | import { useForm, zodResolver } from '@mantine/form' 3 | import { useTranslation } from 'react-i18next' 4 | import { z } from 'zod' 5 | 6 | import { FormActions } from '~/components/FormActions' 7 | import { DEFAULT_TROJAN_FORM_VALUES, trojanSchema } from '~/constants' 8 | import { generateURL } from '~/utils' 9 | 10 | export const TrojanForm = ({ onLinkGeneration }: { onLinkGeneration: (link: string) => void }) => { 11 | const { t } = useTranslation() 12 | const { values, onSubmit, getInputProps, reset } = useForm>({ 13 | initialValues: DEFAULT_TROJAN_FORM_VALUES, 14 | validate: zodResolver(trojanSchema), 15 | }) 16 | 17 | const handleSubmit = onSubmit((values) => { 18 | const query: Record = { 19 | allowInsecure: values.allowInsecure, 20 | } 21 | 22 | if (values.peer !== '') { 23 | query.sni = values.peer 24 | } 25 | 26 | let protocol = 'trojan' 27 | 28 | if (values.method !== 'origin' || values.obfs !== 'none') { 29 | protocol = 'trojan-go' 30 | query.type = values.obfs === 'none' ? 'original' : 'ws' 31 | 32 | if (values.method === 'shadowsocks') { 33 | query.encryption = `ss;${values.ssCipher};${values.ssPassword}` 34 | } 35 | 36 | if (query.type === 'ws') { 37 | query.host = values.host || '' 38 | query.path = values.path || '/' 39 | } 40 | 41 | delete query.allowInsecure 42 | } 43 | 44 | return onLinkGeneration( 45 | generateURL({ 46 | protocol, 47 | username: values.password, 48 | host: values.server, 49 | port: values.port, 50 | hash: values.name, 51 | params: query, 52 | }), 53 | ) 54 | }) 55 | 56 | return ( 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 88 | )} 89 | 90 | {values.method === 'shadowsocks' && ( 91 | 92 | )} 93 | 94 | 99 | 100 | 101 | 102 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | {values.protocol === 'vmess' && } 106 | 107 | {values.protocol === 'vmess' && ( 108 | 131 | )} 132 | 133 | {values.tls !== 'none' && } 134 | 135 | 161 | 162 | {values.net === 'tcp' && ( 163 | 186 | )} 187 | 188 | {(values.net === 'ws' || 189 | values.net === 'h2' || 190 | values.tls === 'tls' || 191 | (values.net === 'tcp' && values.type === 'http')) && ( 192 | 193 | )} 194 | 195 | {values.tls === 'tls' && } 196 | 197 | {(values.net === 'ws' || values.net === 'h2' || (values.net === 'tcp' && values.type === 'http')) && ( 198 | 199 | )} 200 | 201 | {values.net === 'kcp' && } 202 | 203 | {values.net === 'grpc' && } 204 | 205 | 206 | 207 | ) 208 | } 209 | -------------------------------------------------------------------------------- /src/components/ConfigureNodeFormModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { MantineProvider, Modal, Stack, Tabs, TextInput } from '@mantine/core' 2 | import { useForm, zodResolver } from '@mantine/form' 3 | import { useTranslation } from 'react-i18next' 4 | import { z } from 'zod' 5 | 6 | import { useImportNodesMutation } from '~/apis' 7 | 8 | import { HTTPForm } from './HTTPForm' 9 | import { Hysteria2Form } from './Hysteria2Form' 10 | import { JuicityForm } from './JuicityForm' 11 | import { SSForm } from './SSForm' 12 | import { SSRForm } from './SSRForm' 13 | import { Socks5Form } from './Socks5Form' 14 | import { TrojanForm } from './TrojanForm' 15 | import { TuicForm } from './TuicForm' 16 | import { V2rayForm } from './V2rayForm' 17 | 18 | const schema = z.object({ tag: z.string().nonempty() }) 19 | 20 | export const ConfigureNodeFormModal = ({ opened, onClose }: { opened: boolean; onClose: () => void }) => { 21 | const { t } = useTranslation() 22 | const importNodesMutation = useImportNodesMutation() 23 | const form = useForm>({ 24 | initialValues: { tag: '' }, 25 | validate: zodResolver(schema), 26 | }) 27 | 28 | const onLinkGeneration = async (link: string) => { 29 | const { hasErrors } = form.validate() 30 | 31 | if (hasErrors) return 32 | 33 | await importNodesMutation.mutateAsync([ 34 | { 35 | link, 36 | tag: form.values.tag, 37 | }, 38 | ]) 39 | 40 | onClose() 41 | } 42 | 43 | return ( 44 | 45 | 46 | 47 | 61 | 62 | 63 | V2RAY 64 | SS 65 | SSR 66 | Trojan 67 | Juicity 68 | Hysteria2 69 | Tuic 70 | HTTP 71 | SOCKS5 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | ) 131 | } 132 | -------------------------------------------------------------------------------- /src/components/DraggableResourceBadge.tsx: -------------------------------------------------------------------------------- 1 | import { useDraggable } from '@dnd-kit/core' 2 | import { ActionIcon, Badge, Text, Tooltip } from '@mantine/core' 3 | import { IconX } from '@tabler/icons-react' 4 | 5 | import { DraggableResourceType } from '~/constants' 6 | 7 | export const DraggableResourceBadge = ({ 8 | id, 9 | name, 10 | type, 11 | nodeID, 12 | groupID, 13 | subscriptionID, 14 | onRemove, 15 | dragDisabled, 16 | children, 17 | }: { 18 | id: string 19 | name: string 20 | type: DraggableResourceType 21 | nodeID?: string 22 | groupID?: string 23 | subscriptionID?: string 24 | onRemove?: () => void 25 | dragDisabled?: boolean 26 | children?: React.ReactNode 27 | }) => { 28 | const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ 29 | id, 30 | data: { 31 | type, 32 | nodeID, 33 | groupID, 34 | subscriptionID, 35 | }, 36 | disabled: dragDisabled, 37 | }) 38 | 39 | return ( 40 | {children}}> 41 | 47 | 48 | 49 | ) 50 | } 51 | style={{ 52 | zIndex: isDragging ? 10 : 0, 53 | cursor: isDragging ? 'grabbing' : 'grab', 54 | }} 55 | opacity={isDragging ? 0.5 : undefined} 56 | > 57 | 58 | {name} 59 | 60 | 61 | 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /src/components/DraggableResourceCard.tsx: -------------------------------------------------------------------------------- 1 | import { useDraggable } from '@dnd-kit/core' 2 | import { ActionIcon, Badge, Card, Group, Text } from '@mantine/core' 3 | import { modals } from '@mantine/modals' 4 | import { IconTrash } from '@tabler/icons-react' 5 | import React from 'react' 6 | import { useTranslation } from 'react-i18next' 7 | 8 | import { DraggableResourceType } from '~/constants' 9 | 10 | export const DraggableResourceCard = ({ 11 | id, 12 | nodeID, 13 | subscriptionID, 14 | type, 15 | name, 16 | leftSection, 17 | onRemove, 18 | actions, 19 | children, 20 | }: { 21 | id: string 22 | nodeID?: string 23 | subscriptionID?: string 24 | type: DraggableResourceType 25 | name: React.ReactNode 26 | leftSection?: React.ReactNode 27 | onRemove: () => void 28 | actions?: React.ReactNode 29 | children: React.ReactNode 30 | }) => { 31 | const { t } = useTranslation() 32 | const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id, data: { type, nodeID, subscriptionID } }) 33 | 34 | return ( 35 | 44 | 45 | 46 | {leftSection} 47 | 48 | 57 | {name} 58 | 59 | 60 | 61 | {actions} 62 | 63 | { 67 | modals.openConfirmModal({ 68 | title: t('actions.remove'), 69 | labels: { 70 | cancel: t('confirmModal.cancel'), 71 | confirm: t('confirmModal.confirm'), 72 | }, 73 | children: t('confirmModal.removeConfirmDescription'), 74 | onConfirm: onRemove, 75 | }) 76 | }} 77 | > 78 | 79 | 80 | 81 | 82 | 83 | 84 | {children} 85 | 86 | ) 87 | } 88 | -------------------------------------------------------------------------------- /src/components/DroppableGroupCard.tsx: -------------------------------------------------------------------------------- 1 | import { useDroppable } from '@dnd-kit/core' 2 | import { ActionIcon, Card, Group, Title } from '@mantine/core' 3 | import { modals } from '@mantine/modals' 4 | import { IconTrash } from '@tabler/icons-react' 5 | import { useTranslation } from 'react-i18next' 6 | 7 | export const DroppableGroupCard = ({ 8 | id, 9 | name, 10 | onRemove, 11 | actions, 12 | children, 13 | }: { 14 | id: string 15 | name: string 16 | onRemove?: () => void 17 | actions?: React.ReactNode 18 | children?: React.ReactNode 19 | }) => { 20 | const { t } = useTranslation() 21 | const { isOver, setNodeRef } = useDroppable({ id }) 22 | 23 | return ( 24 | 33 | 34 | 35 | {name} 36 | 37 | 38 | {actions} 39 | 40 | {onRemove && ( 41 | { 45 | modals.openConfirmModal({ 46 | title: t('actions.remove'), 47 | labels: { 48 | cancel: t('confirmModal.cancel'), 49 | confirm: t('confirmModal.confirm'), 50 | }, 51 | children: t('confirmModal.removeConfirmDescription'), 52 | onConfirm: onRemove, 53 | }) 54 | }} 55 | > 56 | 57 | 58 | )} 59 | 60 | 61 | 62 | 63 | {children && ( 64 | 65 | {children} 66 | 67 | )} 68 | 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /src/components/ExpandedTableRow.tsx: -------------------------------------------------------------------------------- 1 | import { Divider, Group, Stack, Text } from '@mantine/core' 2 | import { Fragment } from 'react' 3 | 4 | export const ExpandedTableRow = ({ data }: { data: { name: string; value: string }[] }) => ( 5 | 6 | {data.map(({ name, value }, i) => { 7 | return ( 8 | 9 | 10 | {name}: 11 | {value} 12 | 13 | 14 | {i !== data.length - 1 && } 15 | 16 | ) 17 | })} 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /src/components/FormActions.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Group } from '@mantine/core' 2 | import { useTranslation } from 'react-i18next' 3 | 4 | export const FormActions = ({ loading, reset }: { loading?: boolean; reset?: () => void }) => { 5 | const { t } = useTranslation() 6 | 7 | return ( 8 | 9 | 12 | 13 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/components/GroupFormModal.tsx: -------------------------------------------------------------------------------- 1 | import { Modal, Select, Stack, TextInput } from '@mantine/core' 2 | import { UseFormReturnType, useForm, zodResolver } from '@mantine/form' 3 | import { forwardRef, useImperativeHandle, useState } from 'react' 4 | import { useTranslation } from 'react-i18next' 5 | import { z } from 'zod' 6 | 7 | import { useCreateGroupMutation, useGroupSetPolicyMutation } from '~/apis' 8 | import { FormActions } from '~/components/FormActions' 9 | import { DEFAULT_GROUP_POLICY } from '~/constants' 10 | import { Policy } from '~/schemas/gql/graphql' 11 | 12 | import { SelectItemWithDescription } from './SelectItemWithDescription' 13 | 14 | const schema = z.object({ 15 | name: z.string().nonempty(), 16 | policy: z.nativeEnum(Policy), 17 | }) 18 | 19 | export type GroupFormModalRef = { 20 | form: UseFormReturnType> 21 | setEditingID: (id: string) => void 22 | initOrigins: (origins: z.infer) => void 23 | } 24 | 25 | export const GroupFormModal = forwardRef(({ opened, onClose }: { opened: boolean; onClose: () => void }, ref) => { 26 | const { t } = useTranslation() 27 | const [editingID, setEditingID] = useState() 28 | const [origins, setOrigins] = useState>() 29 | const form = useForm>({ 30 | validate: zodResolver(schema), 31 | initialValues: { 32 | name: '', 33 | policy: DEFAULT_GROUP_POLICY, 34 | }, 35 | }) 36 | 37 | const initOrigins = (origins: z.infer) => { 38 | form.setValues(origins) 39 | setOrigins(origins) 40 | } 41 | 42 | useImperativeHandle(ref, () => ({ 43 | form, 44 | setEditingID, 45 | initOrigins, 46 | })) 47 | 48 | const createGroupMutation = useCreateGroupMutation() 49 | const groupSetPolicyMutation = useGroupSetPolicyMutation() 50 | 51 | const policyData = [ 52 | { 53 | label: Policy.MinMovingAvg, 54 | value: Policy.MinMovingAvg, 55 | description: t('descriptions.group.MinMovingAvg'), 56 | }, 57 | { 58 | label: Policy.MinAvg10, 59 | value: Policy.MinAvg10, 60 | description: t('descriptions.group.MinAvg10'), 61 | }, 62 | { 63 | label: Policy.Min, 64 | value: Policy.Min, 65 | description: t('descriptions.group.Min'), 66 | }, 67 | { 68 | label: Policy.Random, 69 | value: Policy.Random, 70 | description: t('descriptions.group.Random'), 71 | }, 72 | { 73 | label: Policy.Fixed, 74 | value: Policy.Fixed, 75 | description: t('descriptions.group.Fixed'), 76 | }, 77 | ] 78 | 79 | return ( 80 | 81 |
{ 83 | const policyParams = values.policy === Policy.Fixed ? [{ key: '', val: '0' }] : [] 84 | 85 | if (editingID) { 86 | await groupSetPolicyMutation.mutateAsync({ 87 | id: editingID, 88 | policy: values.policy, 89 | policyParams: policyParams, 90 | }) 91 | } else { 92 | await createGroupMutation.mutateAsync({ 93 | name: values.name, 94 | policy: values.policy, 95 | policyParams: policyParams, 96 | }) 97 | } 98 | 99 | onClose() 100 | form.reset() 101 | })} 102 | > 103 | 104 | 105 | 106 |