├── .babelrc
├── .dockerignore
├── .editorconfig
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── codeql-analysis.yml
│ └── container.yml
├── .gitignore
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── api
├── gateway.js
├── releases.js
└── tool
│ ├── binwalk.js
│ ├── port-scan.js
│ ├── pyc.js
│ └── zip-pe.js
├── assets
└── css
│ └── main.css
├── components
├── Footer.vue
├── TinyButtonLink.vue
├── TopBar.vue
├── form
│ ├── GridWithDoubleColumns.vue
│ ├── PrimaryButton.vue
│ ├── PrimaryCheckbox.vue
│ ├── PrimaryFileUploader.vue
│ ├── PrimaryInput.vue
│ ├── PrimaryPreBlock.vue
│ ├── PrimarySelector.vue
│ └── PrimaryTextArea.vue
├── settings
│ ├── Area.vue
│ ├── Divider.vue
│ └── Item.vue
├── tool
│ ├── BadgeDot.vue
│ ├── InteractiveBlock.vue
│ ├── InteractiveDoubleColumns.vue
│ ├── ObjectViewer.vue
│ ├── PrimaryContainer.vue
│ ├── PrimaryIntroduction.vue
│ └── Tool.vue
├── uni
│ ├── Button.vue
│ ├── Input.vue
│ ├── Link.vue
│ ├── Select.vue
│ └── Textarea.vue
└── widgets
│ └── CodeBlock.vue
├── content
└── intro
│ ├── brain-fuck.md
│ ├── ceasar-cipher.md
│ ├── crc.md
│ ├── data-unit-conversion.md
│ ├── jsfuck.md
│ ├── jsonpath.md
│ ├── morse-code.md
│ ├── pyc-decompiler.md
│ ├── rail-fence.md
│ └── rot13.md
├── jest.config.js
├── jsconfig.json
├── lang
├── en-US.js
└── zh-CN.js
├── layouts
└── default.vue
├── libs
├── brainfuck.js
├── ceasarCipher.js
├── common.js
├── coreValuesCipher.js
├── crc.js
├── dataUnitConversion.js
├── radixc.js
├── railFence.js
├── serial.js
├── tempc.js
├── utf8-util.js
├── vigenereCipher.js
└── zero-width-steganography.js
├── middleware
└── title-guard.js
├── nuxt.config.js
├── package.json
├── pages
├── _.vue
├── index.vue
├── premium-active.vue
├── settings.vue
├── static
│ └── pigpen
│ │ ├── pigpen-regular.woff
│ │ └── pigpen-regular.woff2
├── tag
│ └── _tag.vue
└── tools
│ ├── ascii.vue
│ ├── base-series.vue
│ ├── base64-to-image.vue
│ ├── bin-extractor.vue
│ ├── brain-fuck.vue
│ ├── caesar-cipher.vue
│ ├── cloud-shadow.vue
│ ├── core-values-cipher.vue
│ ├── crc-checksum.vue
│ ├── data-units-conversion.vue
│ ├── ip-geo.vue
│ ├── jsfuck.vue
│ ├── json-serializer.vue
│ ├── message-digest.vue
│ ├── morse-code.vue
│ ├── pigpen.vue
│ ├── port-scan.vue
│ ├── pseudo-encrypted-zip-check.vue
│ ├── pyc-decompiler.vue
│ ├── radix-conversion.vue
│ ├── rail-fence-cipher.vue
│ ├── rot-series.vue
│ ├── serial.vue
│ ├── temperature-conversion.vue
│ ├── timestamp.vue
│ ├── url-encoding.vue
│ ├── utf8-conversion.vue
│ ├── uuid-generator.vue
│ ├── vigenereCipher.vue
│ └── zero-width-steganography.vue
├── plugins
├── antd-ui.js
├── api.js
├── g-tag.js
└── vue-js-modal.js
├── pnpm-lock.yaml
├── static
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── icon.png
├── icon.svg
├── mstile-150x150.png
├── readme
│ ├── afdian.jpg
│ └── icon_1024.png
├── safari-pinned-tab.svg
├── screenshots
│ ├── footer.png
│ ├── header.png
│ ├── tool_bin_extractor.png
│ ├── tool_pseudo_zip.png
│ └── tool_pyc_decompiler.png
├── site.webmanifest.bak
└── third_party
│ └── workbox
│ ├── workbox-background-sync.dev.js
│ ├── workbox-background-sync.prod.js
│ ├── workbox-broadcast-update.dev.js
│ ├── workbox-broadcast-update.prod.js
│ ├── workbox-cacheable-response.dev.js
│ ├── workbox-cacheable-response.prod.js
│ ├── workbox-core.dev.js
│ ├── workbox-core.prod.js
│ ├── workbox-expiration.dev.js
│ ├── workbox-expiration.prod.js
│ ├── workbox-navigation-preload.dev.js
│ ├── workbox-navigation-preload.prod.js
│ ├── workbox-offline-ga.dev.js
│ ├── workbox-offline-ga.prod.js
│ ├── workbox-precaching.dev.js
│ ├── workbox-precaching.prod.js
│ ├── workbox-range-requests.dev.js
│ ├── workbox-range-requests.prod.js
│ ├── workbox-routing.dev.js
│ ├── workbox-routing.prod.js
│ ├── workbox-strategies.dev.js
│ ├── workbox-strategies.prod.js
│ ├── workbox-streams.dev.js
│ ├── workbox-streams.prod.js
│ ├── workbox-sw.js
│ ├── workbox-window.dev.es5.mjs
│ ├── workbox-window.dev.mjs
│ ├── workbox-window.dev.umd.js
│ ├── workbox-window.prod.es5.mjs
│ ├── workbox-window.prod.mjs
│ └── workbox-window.prod.umd.js
├── store
├── index.js
├── runtime.js
└── settings.js
└── tailwind.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "test": {
4 | "presets": [
5 | [
6 | "@babel/preset-env",
7 | {
8 | "targets": {
9 | "node": "current"
10 | }
11 | }
12 | ]
13 | ]
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | /.github
2 | /.nuxt
3 | /dist
4 | /node_modules
5 | /coverage
6 |
7 | yarn.lock
8 | yarn-error.log
9 |
--------------------------------------------------------------------------------
/.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 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: 5ANK41
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 | - 'https://afdian.net/@hoshino_suzumi'
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: 反馈在使用过程中遇到的问题
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: HoshinoSuzumi
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
40 | **描述问题**
41 | 简单的描述你所遇到的问题
42 |
43 | **复现问题**
44 | 详细写出复现问题的步骤
45 | 1. Go to '...'
46 | 2. Click on '....'
47 | 3. Scroll down to '....'
48 | 4. See error
49 |
50 | **预期表现**
51 | 简单描述你认为正确的表现
52 |
53 | **截图**
54 | 如果有,请上传问题相关的截图
55 |
56 | **环境**
57 | - 操作系统: [e.g. Windows11]
58 | - 浏览器: [e.g. chrome, safari]
59 | - 版本号: [e.g. 100]
60 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: 为这个项目出谋划策
4 | title: "[FEAT] "
5 | labels: feature
6 | assignees: HoshinoSuzumi
7 |
8 | ---
9 |
10 | **描述你的诉求**
11 | 请写出你对这个项目的期望、新功能的需求和提议、现有功能的完善和补充之处
12 |
13 | **描述你想要的解决方案**
14 | 你对这个诉求有什么想法,你希望我们如何实现
15 |
16 | **额外信息(可选)**
17 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '40 2 * * 2'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v2
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v2
71 |
--------------------------------------------------------------------------------
/.github/workflows/container.yml:
--------------------------------------------------------------------------------
1 | name: "Make Docker Image"
2 |
3 | on:
4 | push:
5 | tags:
6 | - "**"
7 |
8 | jobs:
9 | build:
10 | name: "Build Image"
11 | runs-on: ${{ matrix.os }}
12 |
13 | strategy:
14 | matrix:
15 | os: [ ubuntu-latest ]
16 | node: [ 16 ]
17 |
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v3
21 |
22 | - name: Get Tag Name
23 | id: get_version
24 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
25 |
26 | - name: Set up QEMU
27 | uses: docker/setup-qemu-action@v2
28 |
29 | - name: Set up Docker Buildx
30 | uses: docker/setup-buildx-action@v2
31 |
32 | - name: Login to Docker Hub
33 | uses: docker/login-action@v2
34 | with:
35 | username: ${{ secrets.DOCKERHUB_USERNAME }}
36 | password: ${{ secrets.DOCKERHUB_TOKEN }}
37 |
38 | - name: Login to GitHub Container Registry
39 | uses: docker/login-action@v2
40 | with:
41 | registry: ghcr.io
42 | username: ${{ github.repository_owner }}
43 | password: ${{ secrets.GHCR_TOKEN }}
44 |
45 | - name: Create Dotenv File
46 | uses: actually-colab/github-action-create-env-file@v2.3
47 | with:
48 | envkey_CEVER_VERSION: ${{ steps.get_version.outputs.VERSION }}
49 | directory: .
50 | file_name: .env
51 |
52 | - name: Build and push
53 | uses: docker/build-push-action@v4
54 | with:
55 | context: .
56 | platforms: linux/amd64
57 | push: true
58 | outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=The official CTFever frontend image
59 | tags: |
60 | hoshinosuzumi/ctfever:${{ steps.get_version.outputs.VERSION }}
61 | hoshinosuzumi/ctfever:latest
62 | ghcr.io/uniiemstudio/ctfever:${{ steps.get_version.outputs.VERSION }}
63 | ghcr.io/uniiemstudio/ctfever:latest
64 |
65 | deploy:
66 | name: "Trigger automatic pull"
67 | runs-on: ${{ matrix.os }}
68 | needs:
69 | - build
70 |
71 | strategy:
72 | matrix:
73 | os: [ ubuntu-latest ]
74 | node: [ 16 ]
75 |
76 | steps:
77 | - name: Webhook POST Action
78 | # You may pin to the exact commit or the version.
79 | # uses: muinmomin/webhook-action@7c067ef19e5de3aca7aebda32628f1d08d06bf89
80 | uses: muinmomin/webhook-action@v1.0.0
81 | with:
82 | # URL of webhook to send post request to
83 | url: ${{ secrets.DEPLOY_WEBHOOK }}
84 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | /logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 |
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 |
41 | # TypeScript v1 declaration files
42 | typings/
43 |
44 | # Optional npm cache directory
45 | .npm
46 |
47 | # Optional eslint cache
48 | .eslintcache
49 |
50 | # Optional REPL history
51 | .node_repl_history
52 |
53 | # Output of 'npm pack'
54 | *.tgz
55 |
56 | # Yarn Integrity file
57 | .yarn-integrity
58 |
59 | # dotenv environment variables file
60 | .env
61 |
62 | # parcel-bundler cache (https://parceljs.org/)
63 | .cache
64 |
65 | # next.js build output
66 | .next
67 |
68 | # nuxt.js build output
69 | .nuxt
70 |
71 | # Nuxt generate
72 | dist
73 |
74 | # vuepress build output
75 | .vuepress/dist
76 |
77 | # Serverless directories
78 | .serverless
79 |
80 | # IDE / Editor
81 | .idea
82 |
83 | # Service worker
84 | sw.*
85 |
86 | # macOS
87 | .DS_Store
88 |
89 | # Vim swap files
90 | *.swp
91 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # 贡献指南
2 |
3 | ## 新增工具
4 |
5 | 要向 CTFever 添加新工具时,请完善以下信息:
6 |
7 | ### Javascript 库
8 |
9 | `/libs`
10 |
11 | 自定义的 javascript 库存放在 `/libs` 目录下。命名清晰有辨识度,且不与内置的库名称冲突。
12 |
13 | ### 元信息
14 |
15 | `/store/index.js`
16 |
17 | 工具的元信息包含了名称、介绍、标签等信息,使得 CTFever 可以对工具进行分类、筛选和更好地展示
18 |
19 | `toolkits` 数组包含了工具分类的元信息,元素是一个对象,包含了分类名称、分类简介和工具列表。
20 |
21 | `toolkits.tools` 数组包含了工具的元信息,元素是一个对象,包含了工具名称、工具介绍、工具标签和路由。路由必须定义为 `/tools/工具名称(横杠命名)`
22 |
23 | 它们看起来是这个样子
24 |
25 | ```javascript
26 | toolkits: [
27 | {
28 | title: 'toolkit.EaD.title',
29 | description: 'toolkit.EaD.desc',
30 | icon: 'code-outline',
31 | tools: [
32 | {
33 | title: 'tool.ascii.title',
34 | description: 'tool.ascii.desc',
35 | route: '/tools/ascii',
36 | tags: ['字符编码'],
37 | },
38 | // ...
39 | ]
40 | },
41 | // ...
42 | ]
43 | ```
44 |
45 | _元信息的任何部分(path 除外)都应使用 i18n 字符串,而不是直接定义内容_
46 |
47 | ### 国际化 i18n
48 |
49 | `/lang/*.js`
50 |
51 | `en-US` 是必选项,其他语言可选
52 |
53 | ### 工具模板
54 |
55 | `/pages/tools/工具名称(横杠命名).vue`
56 |
57 | 每个工具都应使用这个模板开始
58 |
59 | ```vue
60 |
61 |
62 |
63 |
64 |
65 |
66 |
76 | ```
77 |
78 | 工具页面中的组件尽量使用 `/components/tool` 下的组件确保统一性
79 |
80 | ### 提交新工具
81 |
82 | 推送更改然后 Pull Request 到 UniiemStudio:dev 分支,然后等待我们 review
83 |
84 | **感谢每一位可爱的贡献者**
85 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:16-alpine as build-stage
2 |
3 | WORKDIR /build
4 | COPY . .
5 |
6 | ENV CEVER_RUN_MODE=server
7 | ENV CEVER_BACKEND_BASE=https://ctfever-service.uniiem.com
8 |
9 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
10 | RUN apk add --update --no-cache gcc g++ make cmake curl jq py3-configobj py3-pip py3-setuptools python3 python3-dev
11 |
12 | RUN npm config set registry https://registry.npm.taobao.org && \
13 | yarn && yarn build
14 |
15 | FROM node:16-alpine as production-stage
16 |
17 | LABEL maintainer="hoshinosuzumi"
18 | LABEL org.opencontainers.image.source="https://github.com/UniiemStudio/CTFever"
19 | LABEL org.opencontainers.image.description="The official CTFever frontend image."
20 |
21 | ENV CEVER_RUN_MODE=server
22 | ENV CEVER_BACKEND_BASE=https://ctfever-service.uniiem.com
23 |
24 | WORKDIR /app
25 | COPY --from=build-stage /build .
26 |
27 | EXPOSE 3000
28 |
29 | CMD yarn start
30 |
--------------------------------------------------------------------------------
/api/gateway.js:
--------------------------------------------------------------------------------
1 | export default axios => ({
2 | call: (mod, method, params = null, file = null, progress = true) => {
3 | let formData = new FormData();
4 | if (params) formData.append('args', JSON.stringify(params));
5 | if (file) formData.append('file', file);
6 | return axios.post(`/gateway/call/${mod}?method=${method}`, formData, {progress: progress})
7 | },
8 | })
9 |
--------------------------------------------------------------------------------
/api/releases.js:
--------------------------------------------------------------------------------
1 | import gateway from "~/api/gateway";
2 |
3 | export default axios => ({
4 | releases_behind: (timestamp) => {
5 | return gateway(axios).call('releasenote', 'releases_behind', {timestamp: timestamp}, null, false);
6 | }
7 | })
8 |
--------------------------------------------------------------------------------
/api/tool/binwalk.js:
--------------------------------------------------------------------------------
1 | import gateway from "~/api/gateway";
2 |
3 | export default axios => ({
4 | scan: (file) => {
5 | return gateway(axios).call('binwalker', 'scan', null, file);
6 | },
7 | artifact: (artifact_id, filename) => {
8 | return gateway(axios).call('binwalker', 'artifact', {artifact_id: artifact_id, filename: filename});
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/api/tool/port-scan.js:
--------------------------------------------------------------------------------
1 | import gateway from "~/api/gateway";
2 |
3 | export default axios => ({
4 | scan: (host, ports) => {
5 | return gateway(axios).call('portscan', 'scan', {host: host, ports: ports});
6 | }
7 | })
8 |
--------------------------------------------------------------------------------
/api/tool/pyc.js:
--------------------------------------------------------------------------------
1 | import gateway from "~/api/gateway";
2 |
3 | export default axios => ({
4 | decompile: (pyc_file) => {
5 | return gateway(axios).call('pycdecompile', 'decompile', null, pyc_file);
6 | }
7 | })
8 |
--------------------------------------------------------------------------------
/api/tool/zip-pe.js:
--------------------------------------------------------------------------------
1 | import gateway from "~/api/gateway";
2 |
3 | export default axios => ({
4 | pseudo_check: (zip_file) => {
5 | return gateway(axios).call('ziputil', 'pseudo_check', null, zip_file);
6 | }
7 | })
8 |
--------------------------------------------------------------------------------
/assets/css/main.css:
--------------------------------------------------------------------------------
1 | /*noinspection CssUnknownTarget*/
2 | @import url('https://fonts.font.im/css?family=PT+Mono|PT+Sans|Poppins|Nunito&display=swap');
3 |
4 | @tailwind base;
5 | @tailwind components;
6 | @tailwind utilities;
7 |
--------------------------------------------------------------------------------
/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 |
28 |
37 |
38 |
41 |
--------------------------------------------------------------------------------
/components/TinyButtonLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
38 |
39 |
42 |
--------------------------------------------------------------------------------
/components/form/GridWithDoubleColumns.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/components/form/PrimaryButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/components/form/PrimaryCheckbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
10 |
11 |
12 |
13 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/components/form/PrimaryFileUploader.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
18 |
19 |
20 |
21 |
22 |
无效的文件类型
23 |
{{ label }}
24 |
25 |
29 |
30 |
31 | {{
32 | multiple ? ` ${files && files.length} 个文件` : `${files && files[0].name}`
33 | }}
34 |
35 |
36 |
37 |
38 |
39 |
123 |
124 |
127 |
--------------------------------------------------------------------------------
/components/form/PrimaryInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
20 |
24 |
30 |
31 |
32 |
33 |
34 |
114 |
115 |
138 |
--------------------------------------------------------------------------------
/components/form/PrimaryPreBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
24 |
25 |
28 |
--------------------------------------------------------------------------------
/components/form/PrimarySelector.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
62 |
63 |
66 |
--------------------------------------------------------------------------------
/components/form/PrimaryTextArea.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
16 |
22 |
23 |
24 |
25 |
26 |
83 |
84 |
94 |
--------------------------------------------------------------------------------
/components/settings/Area.vue:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
32 |
34 |
36 |
37 | {{ title }}
38 |
39 |
{{ subtitle }}
40 |
41 |
44 |
45 |
46 | No items
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
--------------------------------------------------------------------------------
/components/settings/Divider.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/components/settings/Item.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 | {{ title }}
24 | {{ subtitle }}
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
--------------------------------------------------------------------------------
/components/tool/BadgeDot.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
38 |
39 |
42 |
--------------------------------------------------------------------------------
/components/tool/InteractiveBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/components/tool/InteractiveDoubleColumns.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/components/tool/ObjectViewer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
8 |
{{ k }}
9 |
{{ v }}
10 |
11 |
12 |
13 | 单击项目可以复制其值
14 |
15 |
16 |
17 |
18 |
19 |
79 |
80 |
83 |
--------------------------------------------------------------------------------
/components/tool/PrimaryContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/components/tool/PrimaryIntroduction.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
{{ title }}
5 |
6 |
10 |
11 |
20 |
21 |
22 |
23 |
60 |
61 |
70 |
--------------------------------------------------------------------------------
/components/tool/Tool.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | {{ $t(tool.title) || tool.title }}
9 | BETA
10 |
11 |
{{ $t(tool.description) || tool.description }}
13 |
14 |
15 |
16 |
17 |
18 |
20 | {{ `${$t(tag)}${k < tool.tags.length - 1 ? ', ' : ''}` }}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
75 |
76 |
128 |
--------------------------------------------------------------------------------
/components/uni/Button.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
53 |
71 |
72 |
73 |
76 |
--------------------------------------------------------------------------------
/components/uni/Link.vue:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
47 |
--------------------------------------------------------------------------------
/components/uni/Textarea.vue:
--------------------------------------------------------------------------------
1 |
88 |
89 |
90 |
91 |
95 |
96 |
108 |
109 |
110 |
111 |
112 |
115 |
--------------------------------------------------------------------------------
/components/widgets/CodeBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
55 |
56 |
59 |
--------------------------------------------------------------------------------
/content/intro/brain-fuck.md:
--------------------------------------------------------------------------------
1 | BrainFuck 是一种极小化的计算机语言,它是由Urban Müller在1993年创建。该语言仅由八个简单命令和一个指令指针组成。虽然它是完全图灵完备的,但它并不是为了实际使用,而是为了挑战和娱乐。
2 |
3 | **符号语法**
4 |
5 | ##### `+` : 递增当前指针指向单元格的值
6 |
7 | ##### `-` : 递减当前指针指向单元格的值
8 |
9 | ##### `>` : 右移指针
10 |
11 | ##### `<` : 左移指针
12 |
13 | ##### `.` : 输出当前指针所指向的单元格值(ASCII)
14 |
15 | ##### `,` : 输入一个值存储在当前指针所指向的单元格
16 |
17 | ##### `[` : 如果当前指针所指向的单元格值**为** 0,则跳转到与之对应的 `]`
18 |
19 | ##### `]` : 如果当前指针所指向的单元格值**不为** 0,则跳转到与之对应的 `[`
20 |
--------------------------------------------------------------------------------
/content/intro/ceasar-cipher.md:
--------------------------------------------------------------------------------
1 | 凯撒密码(英语:Caesar
2 | cipher),或称凯撒加密、凯撒变换、变换加密,是一种最简单且最广为人知的加密技术。凯撒密码是一种替换加密技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是3的时候,所有的字母A将被替换成D,B变成E,以此类推。这个加密方法是以罗马共和时期凯撒的名字命名的,据称当年凯撒曾用此方法与其将军们进行联系。
3 |
--------------------------------------------------------------------------------
/content/intro/crc.md:
--------------------------------------------------------------------------------
1 | **校验方式详解**
2 |
3 | | 校验方式 | 多项式 | 初始值 | 前/后 | 异或值 |
4 | |-------------------|-----------------------------------------------|--------|-----|--------|
5 | | CRC16_CCITT | x16+x12+x5+1(0x1021) | 0x0000 | 低/高 | 0x0000 |
6 | | CRC16_CCITT_FALSE | x16+x12+x5+1(0x1021) | 0xFFFF | 高/低 | 0x0000 |
7 | | CRC16_XMODEM | x16+x12+x5+1(0x1021) | 0x0000 | 高/低 | 0x0000 |
8 | | CRC16_X25 | x16+x12+x5+1(0x1021) | 0xFFFF | 低/高 | 0xFFFF |
9 | | CRC16_MODBUS | x16+x15+x2+1(0x8005) | 0xFFFF | 低/高 | 0x0000 |
10 | | CRC16_IBM | x16+x15+x2+1(0x8005) | 0x0000 | 低/高 | 0x0000 |
11 | | CRC16_MAXIM | x16+x15+x2+1(0x8005) | 0x0000 | 低/高 | 0xFFFF |
12 | | CRC16_USB | x16+x15+x2+1(0x8005) | 0xFFFF | 低/高 | 0xFFFF |
13 | | CRC16_DNP | x16+x13+x12+x11+x10+
x8+x6+x5+x2+1(0x3D65) | 0x0000 | 低/高 | 0xFFFF |
14 |
15 | **校验原理和步骤**
16 |
17 | 1. 预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器;
18 | 2. 把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器,高八位数据不变;
19 | 3. 把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
20 | 4. 如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;
21 | 5. 重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
22 | 6. 重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
23 | 7. 将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换;
24 | 8. 最后得到的CRC寄存器内容即为:CRC码。
25 |
26 |
27 | * 以上计算步骤中的多项式 0xA001 是 0x8005 按位颠倒后的结果。
28 | * 0x8408 是 0x1021 按位颠倒后的结果。
29 |
--------------------------------------------------------------------------------
/content/intro/data-unit-conversion.md:
--------------------------------------------------------------------------------
1 | ## 单位换算关系
2 |
3 | 各单位间的转换关系如下,这里没有列出 P/E/Z/Y 级别的单位,因为目前很少使用,根据此表类推即可:
4 |
5 |
6 |
7 |
8 | ### 十进制单位
9 |
10 | | 全名 | 缩写 | 倍率 |
11 | |----------|----|-------|
12 | | Kilobyte | KB | 10^3 |
13 | | Megabyte | MB | 10^6 |
14 | | Gigabyte | GB | 10^9 |
15 | | Terabyte | TB | 10^12 |
16 |
17 |
18 |
19 |
20 | ### 二进制单位
21 |
22 | | 全名 | 缩写 | 倍率 |
23 | |-----------|-----|------|
24 | | Kibibyte | KiB | 2^10 |
25 | | Mebibyte | MiB | 2^20 |
26 | | Gibibyte | GiB | 2^30 |
27 | | Tebibyte] | TiB | 2^40 |
28 |
29 |
30 |
31 |
32 | ## 数据单位规范
33 |
34 | Byte: 字节,计算机中最小的存储单位,一个字节等于 8 位二进制数,即 2^8 = 256 种状态。
35 |
36 | Bit : 比特,计算机中最小的数据单位,一个比特只能表示 0 或 1 两种状态。
37 |
38 | 1 Byte = 8 Bit
39 |
40 | KiB = "Kibi-" + "Byte" = Kibibyte ("千字节")
41 |
42 | kB = "Kilo-" + "Byte" = Kilobyte ("千"字节)
43 |
44 | kb = "Kilo-" + "bit" = Kilobit ("千"比特)
45 |
46 | 1 KiB = 2^10 Byte = 1024 Byte
47 |
48 | 1 kB = 10^3 Byte = 1000 Byte
49 |
50 | 为了消除歧义,这些规则是在《IEC 60027》第二修正案中被补充的:
51 |
52 | 二进制千字节:kB → KiB
53 |
54 | 二进制兆字节:MB → MiB
55 |
56 | 二进制吉字节:GB → GiB
57 |
58 | 以此类推...
59 |
60 | 即:
61 |
62 | 1 KiB = 1024 B ≠ 1000 B
63 |
64 | 1 kB = 1000 B ≠ 1024 B
65 |
66 | 以此类推...
67 |
--------------------------------------------------------------------------------
/content/intro/jsfuck.md:
--------------------------------------------------------------------------------
1 | JSFuck 是一种深奥的 JavaScript 编程风格。以这种风格写成的代码中仅使用 `[` `]` `(` `)` `!` 和 `+` 六种字符。此编程风格的名字派生自 Brainfuck 语言。与其他深奥的编程语言不同,以
2 | JSFuck 风格写出的代码不需要另外的编译器或解释器来执行,无论浏览器或 JavaScript 引擎中的原生 JavaScript 解释器皆可直接运行。鉴于 JavaScript 是弱类型语言,编写者可以用数量有限的字符重写
3 | JavaScript 中的所有功能,且可以用这种方式执行任何类型的表达式。
4 |
5 | **基本语法**
6 |
7 | | JavaScript | JSFuck |
8 | |------------|-------------------------------------------------|
9 | | false | `![]` |
10 | | true | `!![]` |
11 | | undefined | `[][[]]` |
12 | | NaN | `+[![]]` |
13 | | 0 | `+[]` |
14 | | 1 | `+!+[]` |
15 | | 2 | `!+[]+!+[]` |
16 | | 10 | `[+!+[]]+[+[]]` |
17 | | Array | `[]` |
18 | | Number | `+[]` |
19 | | String | `[]+[]` |
20 | | Boolean | `![]` |
21 | | Function | `[]["filter"]` |
22 | | eval | `[]["filter"]["constructor"] (CODE)()` |
23 | | window | `[]["filter"]["constructor"] ("return this")()` |
24 |
--------------------------------------------------------------------------------
/content/intro/jsonpath.md:
--------------------------------------------------------------------------------
1 | JsonPath 表达式引用 JSON 结构,使用 $ 分配给外层对象的抽象名称。
2 |
3 | | JsonPath 表达式 | 描述 |
4 | |------------------|---------------------------|
5 | | $ | 根对象或元素 |
6 | | @ | 当前对象或元素 |
7 | | . or [] | 子元素操作符 |
8 | | .. | 递归子元素 |
9 | | * | 通配符,匹配所有对象或元素 |
10 | | [] | 下标运算符 |
11 | | [,] | 连接运算符,将多个结果拼接为数组返回,允许使用别名 |
12 | | [start,end,step] | 数组切片 |
13 | | ?() | 过滤器(脚本)表达式 |
14 | | () | 脚本表达式 |
15 |
--------------------------------------------------------------------------------
/content/intro/morse-code.md:
--------------------------------------------------------------------------------
1 | 摩尔斯电码也被称作摩斯密码,是一种时通时断的信号代码,通过不同的排列顺序来表达不同的英文字母、数字和标点符号。它发明于1837年,是一种早期的数字化通信形式。不同于现代化的数字通讯,摩尔斯电码只使用零和一两种状态的二进制代码,它的代码包括五种:短促的点信号`・`
2 | ,保持一定时间的长信号`-`,表示点和划之间的停顿、每个词之间中等的停顿,以及句子之间长的停顿。
3 |
4 | **摩尔斯电码表**
5 |
6 | | 字符 | 电码 | 字符 | 电码 | 字符 | 电码 |
7 | |-----|--------|-----|--------|-----|----------|
8 | | A | `・-` | N | `-・` | 1 | `・----` |
9 | | B | `-・・・` | O | `---` | 2 | `・・---` |
10 | | C | `-・-・` | P | `・--・` | 3 | `・・・--` |
11 | | D | `-・・` | Q | `--・-` | 4 | `・・・・-` |
12 | | E | `・` | R | `・-・` | 5 | `・・・・・` |
13 | | F | `・・-・` | S | `・・・` | 6 | `-・・・・` |
14 | | G | `--・` | T | `-` | 7 | `--・・・` |
15 | | H | `・・・・` | U | `・・-` | 8 | `---・・` |
16 | | I | `・・` | V | `・・・-` | 9 | `----・` |
17 | | J | `・---` | W | `・--` | 0 | `-----` |
18 | | K | `-・-` | X | `-・・-` | ? | `・・--・・` |
19 | | L | `・-・・` | Y | `-・--` | / | `-・・-・` |
20 | | M | `--` | Z | `--・・` | () | `-・--・-` |
21 | | | | | | - | `-・・・・-` |
22 | | | | | | ・ | `・-・-・-` |
23 |
--------------------------------------------------------------------------------
/content/intro/pyc-decompiler.md:
--------------------------------------------------------------------------------
1 | **反编译工具**
2 |
3 | 此工具使用 CTF 比赛中惯用的两款开源反编译库,根据两款库对 Python 版本不同程度的支持,选择合适的工具进行反编译。
4 |
5 | * **Python 3.9 及以上** `Decompyle++ (pycdc)`
6 | * **Python 3.9 以下** `decompyle3`
7 |
8 | **反编译过程**
9 |
10 | * 提取 `magic number` (文件首 4 字节) 识别 Python 版本
11 | * 根据 Python 版本选择反编译工具
12 | * 返回反编译结果
13 |
14 |
--------------------------------------------------------------------------------
/content/intro/rail-fence.md:
--------------------------------------------------------------------------------
1 | **W 型栅栏密码**
2 |
--------------------------------------------------------------------------------
/content/intro/rot13.md:
--------------------------------------------------------------------------------
1 | 回转 13 位密码,是一种简易的替换式密码。ROT13 是明文本身的逆反,所以 ROT13 加密和解密套用同一种算法可得
2 |
3 | **ROT13 查找表**
4 |
5 | ##### `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`
6 |
7 | ##### `NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm`
8 |
9 | 实际上 ROT13 就是偏移量为 13 的凯撒密码
10 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleNameMapper: {
3 | '^@/(.*)$': '/$1',
4 | '^~/(.*)$': '/$1',
5 | '^vue$': 'vue/dist/vue.common.js'
6 | },
7 | moduleFileExtensions: [
8 | 'js',
9 | 'vue',
10 | 'json'
11 | ],
12 | transform: {
13 | '^.+\\.js$': 'babel-jest',
14 | '.*\\.(vue)$': 'vue-jest'
15 | },
16 | collectCoverage: true,
17 | collectCoverageFrom: [
18 | '/components/**/*.vue',
19 | '/pages/**/*.vue'
20 | ],
21 | testEnvironment: 'jsdom'
22 | }
23 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "~/*": [
6 | "./*"
7 | ],
8 | "@/*": [
9 | "./*"
10 | ],
11 | "~~/*": [
12 | "./*"
13 | ],
14 | "@@/*": [
15 | "./*"
16 | ]
17 | }
18 | },
19 | "exclude": [
20 | "node_modules",
21 | ".nuxt",
22 | "dist"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/libs/brainfuck.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | let parse = (function () {
4 | let input;
5 | let output;
6 | let memory;
7 | let ptr;
8 | let debug = false;
9 |
10 | let ops = {
11 | '+': function () {
12 | memory[ptr] = memory[ptr] || 0;
13 | memory[ptr] < 255 ? memory[ptr]++ : memory[ptr] = 0;
14 | debug && console.log('+', memory[ptr], ptr, memory);
15 | },
16 |
17 | '-': function () {
18 | memory[ptr] = memory[ptr] || 0;
19 | memory[ptr]--;
20 | debug && console.log('-', memory[ptr], ptr, memory);
21 | },
22 |
23 | '<': function () {
24 | ptr--;
25 | if (ptr < 0) {
26 | ptr = 0;
27 | }
28 | debug && console.log('<', ptr, memory);
29 | },
30 |
31 | '>': function () {
32 | ptr++;
33 | debug && console.log('>', ptr, memory);
34 | },
35 |
36 | '.': function () {
37 | let c = String.fromCharCode(memory[ptr]);
38 | output.push(c);
39 | debug && console.log('.', c, memory[ptr], memory);
40 | },
41 |
42 | ',': function () {
43 | let c = input.shift();
44 | if (typeof c == "string") {
45 | memory[ptr] = c.charCodeAt(0);
46 | }
47 | debug && console.log(',', c, memory[ptr], memory);
48 | },
49 | };
50 |
51 | function program(nodes) {
52 | return function (inputString) {
53 | output = [];
54 | memory = [];
55 | ptr = 0;
56 |
57 | input = inputString && inputString.split('') || [];
58 |
59 | nodes.forEach(function (node) {
60 | node();
61 | });
62 |
63 | return output.join('');
64 | }
65 | }
66 |
67 | function loop(nodes) {
68 | return function () {
69 | let loopCounter = 0;
70 |
71 | while (memory[ptr] > 0) {
72 | if (loopCounter++ > 20000) {
73 | throw "Infinite loop detected";
74 | }
75 |
76 | nodes.forEach(function (node) {
77 | node();
78 | });
79 | }
80 | };
81 | }
82 |
83 |
84 | let programChars;
85 |
86 | function parseProgram() {
87 | let nodes = [];
88 | let nextChar;
89 |
90 | while (programChars.length > 0) {
91 | nextChar = programChars.shift();
92 | if (ops[nextChar]) {
93 | nodes.push(ops[nextChar]);
94 | } else if (nextChar === '[') {
95 | nodes.push(parseLoop());
96 | } else if (nextChar === ']') {
97 | throw "Missing opening bracket";
98 | } else {
99 | // ignore it
100 | }
101 | }
102 |
103 | return program(nodes);
104 | }
105 |
106 | function parseLoop() {
107 | let nodes = [];
108 | let nextChar;
109 |
110 | while (programChars[0] !== ']') {
111 | nextChar = programChars.shift();
112 | if (nextChar === undefined) {
113 | throw "Missing closing bracket";
114 | } else if (ops[nextChar]) {
115 | nodes.push(ops[nextChar]);
116 | } else if (nextChar === '[') {
117 | nodes.push(parseLoop());
118 | } else {
119 | // ignore
120 | }
121 | }
122 | programChars.shift();
123 |
124 | return loop(nodes);
125 | }
126 |
127 | function parse(str) {
128 | programChars = str.split('');
129 | return parseProgram();
130 | }
131 |
132 | return parse;
133 | })();
134 |
135 |
136 | // function runBrainFuck(code, input) {
137 | // return parse(code)(input);
138 | // }
139 |
140 | function runBrainFuck(code, input) {
141 | return new Promise((resolve, reject) => {
142 | let result;
143 | result = parse(code)(input);
144 | resolve(result);
145 | });
146 | }
147 |
148 | module.exports = runBrainFuck;
149 |
--------------------------------------------------------------------------------
/libs/ceasarCipher.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function encode(r, a) {
4 | let u = [];
5 | let i = [[48, 57], [65, 90], [97, 122], [19968, 40869]];
6 | let t;
7 | let e;
8 | let s;
9 | let o;
10 | let n;
11 | for (let f = 0; f < r.length; f++) {
12 | e = r.substr(f, 1);
13 | s = !1;
14 | n = e.charCodeAt(0);
15 | for (const v in i) if (n >= i[v][0] && n <= i[v][1]) {
16 | t = a % (i[v][1] - i[v][0] + 1), t < 0 && (t = i[v][1] - i[v][0] + 1 + t), o = n + t, o > i[v][1] && (o = i[v][0] + (o - i[v][1] - 1)), u.push(String.fromCharCode(o)), s = !0;
17 | break
18 | }
19 | s || u.push(e)
20 | }
21 | return u.join("")
22 | }
23 |
24 | function decode(r, a) {
25 | let u = [];
26 | let n;
27 | let s;
28 | let e;
29 | let t;
30 | let i = [[48, 57], [65, 90], [97, 122], [19968, 40869]];
31 | let o;
32 | for (let f = 0; f < r.length; f++) {
33 | e = r.substr(f, 1);
34 | s = !1;
35 | n = e.charCodeAt(0);
36 | for (const v in i) if (n >= i[v][0] && n <= i[v][1]) {
37 | t = a % (i[v][1] - i[v][0] + 1), t < 0 && (t = i[v][1] - i[v][0] + 1 + t), o = n - t, o < i[v][0] && (o = i[v][1] - (i[v][0] - o - 1)), u.push(String.fromCharCode(o)), s = !0;
38 | break
39 | }
40 | s || u.push(e)
41 | }
42 | return u.join("")
43 | }
44 |
45 | const ceasarCipher = {
46 | encode,
47 | decode
48 | };
49 |
50 | module.exports = ceasarCipher;
51 |
--------------------------------------------------------------------------------
/libs/common.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const state = require('~/store').state();
4 |
5 | const wrapI18nPath2MetaRoute = (pathWithI18n) => {
6 | if (pathWithI18n.endsWith('/')) {
7 | pathWithI18n = pathWithI18n.slice(0, -1);
8 | }
9 | return `/${pathWithI18n.split('/').slice(-2).join('/')}`;
10 | }
11 |
12 | const getToolByRoute = (route) => {
13 | const fallback = {};
14 | let tools = [];
15 | state.toolkits.forEach(toolkit => tools.push(toolkit.tools.filter(t => t.route === wrapI18nPath2MetaRoute(route))));
16 | if (tools.filter(t => t.length > 0).length > 0) {
17 | return tools.filter(t => t.length > 0)[0][0] || fallback;
18 | }
19 | return fallback;
20 | }
21 |
22 | const copyTextToClipboard = (text, callback) => {
23 | navigator.clipboard.writeText(text)
24 | .then(r => {
25 | if (callback) callback(r);
26 | })
27 | .catch(e => {
28 | console.error(e);
29 | if (callback) callback(false);
30 | });
31 | }
32 |
33 | module.exports = {
34 | wrapI18nPath2MetaRoute,
35 | getToolByRoute,
36 | copyTextToClipboard
37 | }
38 |
--------------------------------------------------------------------------------
/libs/coreValuesCipher.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function assert(...express) {
4 | const l = express.length;
5 | const msg = (typeof express[l - 1] === 'string') ? express[l - 1] : 'Assert Error';
6 | for (let b of express) {
7 | if (!b) {
8 | throw new Error(msg);
9 | }
10 | }
11 | }
12 |
13 | function randBin() {
14 | return Math.random() >= 0.5;
15 | }
16 |
17 | const values = '富强民主文明和谐自由平等公正法治爱国敬业诚信友善';
18 |
19 | function str2utf8(str) {
20 | const notEncoded = /[A-Za-z\d\-_.!~*'()]/g;
21 | const str1 = str.replace(notEncoded, c => c.codePointAt(0).toString(16));
22 | let str2 = encodeURIComponent(str1);
23 | return str2.replace(/%/g, '').toUpperCase();
24 | }
25 |
26 | function utf82str(utfs) {
27 | assert(typeof utfs === 'string', 'utfs Error');
28 | const l = utfs.length;
29 | assert((l & 1) === 0);
30 | const split = [];
31 | for (let i = 0; i < l; i++) {
32 | if ((i & 1) === 0) {
33 | split.push('%');
34 | }
35 | split.push(utfs[i]);
36 | }
37 | return decodeURIComponent(split.join(''));
38 | }
39 |
40 | function hex2duo(hexs) {
41 | // duodecimal in array of number
42 | // '0'.. '9' -> 0.. 9
43 | // 'A'.. 'F' -> 10, c - 10 a2fFlag = 10
44 | // or 11, c - 6 a2fFlag = 11
45 | assert(typeof hexs === 'string')
46 | const duo = [];
47 | for (let c of hexs) {
48 | const n = Number.parseInt(c, 16);
49 | if (n < 10) {
50 | duo.push(n);
51 | } else {
52 | if (randBin()) {
53 | duo.push(10);
54 | duo.push(n - 10);
55 | } else {
56 | duo.push(11);
57 | duo.push(n - 6);
58 | }
59 | }
60 | }
61 | return duo;
62 | }
63 |
64 | function duo2hex(duo) {
65 | assert(duo instanceof Array);
66 | const hex = [];
67 | const l = duo.length;
68 | let i = 0;
69 | while (i < l) {
70 | if (duo[i] < 10) {
71 | hex.push(duo[i]);
72 | } else {
73 | if (duo[i] === 10) {
74 | i++;
75 | hex.push(duo[i] + 10);
76 | } else {
77 | i++;
78 | hex.push(duo[i] + 6);
79 | }
80 | }
81 | i++;
82 | }
83 | // noinspection JSCheckFunctionSignatures
84 | return hex.map(v => v.toString(16).toUpperCase()).join('');
85 | }
86 |
87 |
88 | function duo2values(duo) {
89 | return duo.map(d => values[2 * d] + values[2 * d + 1]).join('');
90 | }
91 |
92 | function valuesDecode(cipher) {
93 | const duo = [];
94 | for (let c of cipher) {
95 | const i = values.indexOf(c);
96 | if (i === -1) {
97 | } else if (i & 1) {
98 | } else {
99 | duo.push(i >> 1);
100 | }
101 | }
102 | const hexs = duo2hex(duo);
103 | assert((hexs.length & 1) === 0);
104 | let str;
105 | try {
106 | str = utf82str(hexs);
107 | } catch (e) {
108 | throw e;
109 | }
110 | return str;
111 | }
112 |
113 | function valuesEncode(str) {
114 | return duo2values(hex2duo(str2utf8(str)));
115 | }
116 |
117 | module.exports = {
118 | coreValuesEncode: valuesEncode, coreValuesDecode: valuesDecode
119 | }
120 |
--------------------------------------------------------------------------------
/libs/crc.js:
--------------------------------------------------------------------------------
1 | // noinspection DuplicatedCode
2 |
3 | "use strict";
4 |
5 | function CRC(data) {
6 | const crcHandler = {};
7 | if (typeof data === 'string') {
8 | data = data.split("").map(c => c.charCodeAt(0));
9 | }
10 | crcHandler.CRC16_CCITT = () => {
11 | let crc = 0x0000;
12 | for (let i = 0; i < data.length; i++) {
13 | crc = crc ^ data[i];
14 | for (let j = 0; j < 8; j++) {
15 | if ((crc & 0x0001) !== 0) {
16 | crc = crc >>> 1 ^ 0x8408;
17 | } else {
18 | crc = crc >>> 1;
19 | }
20 | }
21 | }
22 | return crc ^ 0x0000;
23 | };
24 | crcHandler.CRC16_CCITT_FALSE = () => {
25 | let crc = 0xffff;
26 | for (let i = 0; i < data.length; i++) {
27 | for (let j = 0; j < 8; j++) {
28 | let bit = ((data[i] >>> (7 - j) & 0x01) === 0x01);
29 | let c15 = ((crc >>> 15 & 0x01) === 0x01);
30 | crc = crc << 1;
31 | if (c15 ^ bit) {
32 | crc = crc ^ 0x1021;
33 | }
34 | }
35 | }
36 | crc = crc & 0xffff;
37 | return crc ^ 0x0000;
38 | };
39 | crcHandler.CRC16_XMODEM = () => {
40 | let crc = 0x0000;
41 | for (let i = 0; i < data.length; i++) {
42 | for (let j = 0; j < 8; j++) {
43 | let bit = ((data[i] >>> (7 - j) & 0x01) === 0x01);
44 | let c15 = ((crc >>> 15 & 0x01) === 0x01);
45 | crc = crc << 1;
46 | if (c15 ^ bit) {
47 | crc = crc ^ 0x1021;
48 | }
49 | }
50 | }
51 | crc = crc & 0xffff;
52 | return crc ^ 0x0000;
53 | };
54 | crcHandler.CRC16_X25 = () => {
55 | let crc = 0xffff;
56 | for (let i = 0; i < data.length; i++) {
57 | crc = crc ^ data[i];
58 | for (let j = 0; j < 8; j++) {
59 | if ((crc & 0x0001) !== 0) {
60 | crc = crc >>> 1 ^ 0x8408;
61 | } else {
62 | crc = crc >>> 1;
63 | }
64 | }
65 | }
66 | return crc ^ 0xffff;
67 | }
68 | crcHandler.CRC16_MODBUS = () => {
69 | let crc = 0xffff;
70 | for (let i = 0; i < data.length; i++) {
71 | crc = crc ^ data[i];
72 | for (let j = 0; j < 8; j++) {
73 | if ((crc & 0x0001) !== 0) {
74 | crc = crc >>> 1 ^ 0xa001;
75 | } else {
76 | crc = crc >>> 1;
77 | }
78 | }
79 | }
80 | return crc ^ 0x0000;
81 | }
82 | crcHandler.CRC16_IBM = () => {
83 | let crc = 0x0000;
84 | for (let i = 0; i < data.length; i++) {
85 | crc = crc ^ data[i];
86 | for (let j = 0; j < 8; j++) {
87 | if ((crc & 0x0001) !== 0) {
88 | crc = crc >>> 1 ^ 0xa001;
89 | } else {
90 | crc = crc >>> 1;
91 | }
92 | }
93 | }
94 | return crc ^ 0x0000;
95 | }
96 | crcHandler.CRC16_MAXIM = () => {
97 | let crc = 0x0000;
98 | for (let i = 0; i < data.length; i++) {
99 | crc = crc ^ data[i];
100 | for (let j = 0; j < 8; j++) {
101 | if ((crc & 0x0001) !== 0) {
102 | crc = crc >>> 1 ^ 0xa001;
103 | } else {
104 | crc = crc >>> 1;
105 | }
106 | }
107 | }
108 | return crc ^ 0xffff;
109 | }
110 | crcHandler.CRC16_USB = () => {
111 | let crc = 0xffff;
112 | for (let i = 0; i < data.length; i++) {
113 | crc = crc ^ data[i];
114 | for (let j = 0; j < 8; j++) {
115 | if ((crc & 0x0001) !== 0) {
116 | crc = crc >>> 1 ^ 0xa001;
117 | } else {
118 | crc = crc >>> 1;
119 | }
120 | }
121 | }
122 | return crc ^ 0xffff;
123 | }
124 | crcHandler.CRC16_DNP = () => {
125 | let crc = 0x0000;
126 | for (let i = 0; i < data.length; i++) {
127 | crc = crc ^ data[i];
128 | for (let j = 0; j < 8; j++) {
129 | if ((crc & 0x0001) !== 0) {
130 | crc = crc >>> 1 ^ 0xa6bc;
131 | } else {
132 | crc = crc >>> 1;
133 | }
134 | }
135 | }
136 | return crc ^ 0xffff;
137 | }
138 |
139 | return crcHandler;
140 | }
141 |
142 | module.exports = CRC;
143 |
--------------------------------------------------------------------------------
/libs/dataUnitConversion.js:
--------------------------------------------------------------------------------
1 | const DATA_UNITS = Object.freeze({
2 | BYTE: 'B', // 8 bits, standard unit
3 | // Bit units (IEC) - https://en.wikipedia.org/wiki/Bit
4 | BIT: 'b',
5 | KILOBIT: 'kbit', // 10^3 bits
6 | MEGABIT: 'Mbit', // 10^6 bits
7 | GIGABIT: 'Gbit', // 10^9 bits
8 | TERABIT: 'Tbit', // 10^12 bits
9 | PETABIT: 'Pbit', // 10^15 bits
10 | EXABIT: 'Ebit', // 10^18 bits
11 | ZETTABIT: 'Zbit', // 10^21 bits
12 | YOTTABIT: 'Ybit', // 10^24 bits
13 | // Decimal units (SI) - https://en.wikipedia.org/wiki/Byte
14 | KILOBYTE: 'KB', // 10^3 bytes
15 | MEGABYTE: 'MB', // 10^6 bytes
16 | GIGABYTE: 'GB', // 10^9 bytes
17 | TERABYTE: 'TB', // 10^12 bytes
18 | PETABYTE: 'PB', // 10^15 bytes
19 | EXABYTE: 'EB', // 10^18 bytes
20 | ZETTABYTE: 'ZB', // 10^21 bytes
21 | YOTTABYTE: 'YB', // 10^24 bytes
22 | // Binary units (IEC) - https://en.wikipedia.org/wiki/Byte
23 | KIBIBYTE: 'KiB', // 2^10 bytes
24 | MEBIBYTE: 'MiB', // 2^20 bytes
25 | GIBIBYTE: 'GiB', // 2^30 bytes
26 | TEBIBYTE: 'TiB', // 2^40 bytes
27 | PEBIBYTE: 'PiB', // 2^50 bytes
28 | EXBIBYTE: 'EiB', // 2^60 bytes
29 | ZEBIBYTE: 'ZiB', // 2^70 bytes
30 | YOBIBYTE: 'YiB', // 2^80 bytes
31 | })
32 |
33 | const byte = (size, originUnit) => {
34 | const ANY_TO_BYTE = Object.freeze({
35 | [DATA_UNITS.BYTE]: 1,
36 | [DATA_UNITS.BIT]: 1 / 8,
37 | [DATA_UNITS.KILOBIT]: 10 ** 3 / 8,
38 | [DATA_UNITS.MEGABIT]: 10 ** 6 / 8,
39 | [DATA_UNITS.GIGABIT]: 10 ** 9 / 8,
40 | [DATA_UNITS.TERABIT]: 10 ** 12 / 8,
41 | [DATA_UNITS.PETABIT]: 10 ** 15 / 8,
42 | [DATA_UNITS.EXABIT]: 10 ** 18 / 8,
43 | [DATA_UNITS.ZETTABIT]: 10 ** 21 / 8,
44 | [DATA_UNITS.YOTTABIT]: 10 ** 24 / 8,
45 | [DATA_UNITS.KILOBYTE]: 10 ** 3,
46 | [DATA_UNITS.MEGABYTE]: 10 ** 6,
47 | [DATA_UNITS.GIGABYTE]: 10 ** 9,
48 | [DATA_UNITS.TERABYTE]: 10 ** 12,
49 | [DATA_UNITS.PETABYTE]: 10 ** 15,
50 | [DATA_UNITS.EXABYTE]: 10 ** 18,
51 | [DATA_UNITS.ZETTABYTE]: 10 ** 21,
52 | [DATA_UNITS.YOTTABYTE]: 10 ** 24,
53 | [DATA_UNITS.KIBIBYTE]: 2 ** 10,
54 | [DATA_UNITS.MEBIBYTE]: 2 ** 20,
55 | [DATA_UNITS.GIBIBYTE]: 2 ** 30,
56 | [DATA_UNITS.TEBIBYTE]: 2 ** 40,
57 | [DATA_UNITS.PEBIBYTE]: 2 ** 50,
58 | [DATA_UNITS.EXBIBYTE]: 2 ** 60,
59 | [DATA_UNITS.ZEBIBYTE]: 2 ** 70,
60 | [DATA_UNITS.YOBIBYTE]: 2 ** 80,
61 | })
62 | const BYTE_TO_OTHERS = Object.freeze({
63 | [DATA_UNITS.BYTE]: 1,
64 | [DATA_UNITS.BIT]: 8,
65 | [DATA_UNITS.KILOBIT]: 8 / 10 ** 3,
66 | [DATA_UNITS.MEGABIT]: 8 / 10 ** 6,
67 | [DATA_UNITS.GIGABIT]: 8 / 10 ** 9,
68 | [DATA_UNITS.TERABIT]: 8 / 10 ** 12,
69 | [DATA_UNITS.PETABIT]: 8 / 10 ** 15,
70 | [DATA_UNITS.EXABIT]: 8 / 10 ** 18,
71 | [DATA_UNITS.ZETTABIT]: 8 / 10 ** 21,
72 | [DATA_UNITS.YOTTABIT]: 8 / 10 ** 24,
73 | [DATA_UNITS.KILOBYTE]: 1 / 10 ** 3,
74 | [DATA_UNITS.MEGABYTE]: 1 / 10 ** 6,
75 | [DATA_UNITS.GIGABYTE]: 1 / 10 ** 9,
76 | [DATA_UNITS.TERABYTE]: 1 / 10 ** 12,
77 | [DATA_UNITS.PETABYTE]: 1 / 10 ** 15,
78 | [DATA_UNITS.EXABYTE]: 1 / 10 ** 18,
79 | [DATA_UNITS.ZETTABYTE]: 1 / 10 ** 21,
80 | [DATA_UNITS.YOTTABYTE]: 1 / 10 ** 24,
81 | [DATA_UNITS.KIBIBYTE]: 1 / 2 ** 10,
82 | [DATA_UNITS.MEBIBYTE]: 1 / 2 ** 20,
83 | [DATA_UNITS.GIBIBYTE]: 1 / 2 ** 30,
84 | [DATA_UNITS.TEBIBYTE]: 1 / 2 ** 40,
85 | [DATA_UNITS.PEBIBYTE]: 1 / 2 ** 50,
86 | [DATA_UNITS.EXBIBYTE]: 1 / 2 ** 60,
87 | [DATA_UNITS.ZEBIBYTE]: 1 / 2 ** 70,
88 | [DATA_UNITS.YOBIBYTE]: 1 / 2 ** 80,
89 | })
90 | return {
91 | to: (targetUnit, toFixed = 2) => {
92 | return [
93 | parseFloat((size * ANY_TO_BYTE[originUnit] * BYTE_TO_OTHERS[targetUnit]).toFixed(toFixed)),
94 | targetUnit
95 | ]
96 | }
97 | }
98 | }
99 |
100 | module.exports = {DATA_UNITS, byte}
101 |
--------------------------------------------------------------------------------
/libs/radixc.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const BigInt = require('bigi');
4 |
5 | function radixc(str, from, to) {
6 | if (typeof str !== 'string') str = str + '';
7 | if (from === to) {
8 | return str.toUpperCase();
9 | }
10 | if (from === '10') {
11 | return BigInt(str).toString(to).toUpperCase();
12 | }
13 | if (to === '10') {
14 | return BigInt(str, from).toString().toUpperCase();
15 | }
16 | return BigInt(str, from).toString(to).toUpperCase();
17 | }
18 |
19 | /**
20 | * @returns {string} Human-readable string
21 | * @param {number} n Decimal number
22 | */
23 | function decimal_to_readable(n) {
24 | // const u = "BKMGT";
25 | const u = ['B', 'KB', 'MB', 'GB', 'TB'];
26 | let t = "";
27 | let r = n;
28 | let i = 0;
29 | while (r > 0) {
30 | t = (r % 1024) + u[i] + ' ' + t;
31 | r = Math.trunc(r / 1024);
32 | i++;
33 | }
34 | return t.trim();
35 | }
36 |
37 |
38 | /**
39 | * @returns {number|null} Decimal number
40 | * @param {string} s Human-readable string
41 | * similar to '12GB 34MB 56KB 78B','12GiB 34MiB 56KiB 78B','12G34M56K78B'
42 | */
43 | function readable_to_decimal(s) {
44 | s = s.split(' ').join('').toUpperCase()
45 | .replaceAll('IB', '')
46 | .replaceAll('KB', 'K')
47 | .replaceAll('MB', 'M')
48 | .replaceAll('GB', 'G')
49 | .replaceAll('TB', 'T');
50 | let n = 0;
51 | let j = 0;
52 | for (let i = 0; i < s.length; i++) {
53 | switch (s[i]) {
54 | case "B":
55 | n += +s.slice(j, i);
56 | j = i + 1;
57 | break;
58 | case "K":
59 | n += +s.slice(j, i) * 1024;
60 | j = i + 1;
61 | break;
62 | case "M":
63 | n += +s.slice(j, i) * 1024 * 1024;
64 | j = i + 1;
65 | break;
66 | case "G":
67 | n += +s.slice(j, i) * 1024 * 1024 * 1024;
68 | j = i + 1;
69 | break;
70 | case "T":
71 | n += +s.slice(j, i) * 1024 * 1024 * 1024 * 1024;
72 | j = i + 1;
73 | break;
74 | default:
75 | // should we throw error here?
76 | // throw new Error('Unexpected character in readable string')
77 | }
78 | }
79 | return n;
80 | }
81 |
82 | module.exports = {radixc, decimal_to_readable, readable_to_decimal};
83 |
--------------------------------------------------------------------------------
/libs/railFence.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function RailFenceW(content) {
4 | const railFence = {};
5 | railFence['encode'] = (rows) => {
6 | rows = rows || 3;
7 | let fence = [];
8 | for (let i = 0; i < rows; i++) fence.push([]);
9 | let rail = 0;
10 | let change = 1;
11 | for (let char of content.split("")) {
12 | fence[rail].push(char);
13 | rail += change;
14 | if (rail === rows - 1 || rail === 0) change = -change;
15 | }
16 | let r = '';
17 | for (let rail of fence) r += rail.join("");
18 | return r;
19 | }
20 |
21 | railFence['decode'] = (rows) => {
22 | rows = rows || 3;
23 | let fence = [];
24 | for (let i = 0; i < rows; i++) fence.push([]);
25 | let rail = 0;
26 | let change = 1;
27 | content.split("").forEach(char => {
28 | fence[rail].push(char)
29 | rail += change;
30 | if (rail === rows - 1 || rail === 0) change = -change;
31 | });
32 | const rFence = [];
33 | for (let i = 0; i < rows; i++) rFence.push([]);
34 | let i = 0;
35 | let s = content.split("");
36 | for (r of fence) {
37 | for (let j = 0; j < r.length; j++) rFence[i].push(s.shift());
38 | i++;
39 | }
40 | rail = 0;
41 | change = 1;
42 | let r = "";
43 | for (let i = 0; i < content.length; i++) {
44 | r += rFence[rail].shift();
45 | rail += change;
46 | if (rail === rows - 1 || rail === 0) change = -change;
47 | }
48 | return r;
49 | }
50 |
51 | return railFence;
52 | }
53 |
54 | function RailFence(content) {
55 | const railFence = {};
56 | railFence['encode'] = (rows) => {
57 | return 'This feature is unavailable this time. Pull Request is welcome :)';
58 | }
59 | railFence['decode'] = (rows) => {
60 | return 'This feature is unavailable this time. Pull Request is welcome :)';
61 | }
62 | return railFence;
63 | }
64 |
65 | module.exports = {RailFence, RailFenceW};
66 |
--------------------------------------------------------------------------------
/libs/serial.js:
--------------------------------------------------------------------------------
1 | // noinspection JSValidateTypes,JSUnresolvedFunction
2 |
3 | const Serial = function (onRecv, onError, onOpen = null, onClose = null) {
4 | const serial = this;
5 | serial._baud = 115200;
6 | serial._port = null;
7 |
8 | serial._textDecoder = null;
9 | serial._readableStreamClosed = null;
10 | serial._reader = null;
11 |
12 | serial._textEncoder = null;
13 | serial._writableStreamClosed = null;
14 | serial._writer = null;
15 |
16 | serial._onRecv = onRecv;
17 | serial._onError = onError;
18 | serial._onOpen = onOpen;
19 | serial._onClose = onClose;
20 |
21 | serial.open = async function (baud = 115200) {
22 | serial._baud = baud;
23 | try {
24 | serial._port = await navigator.serial.requestPort();
25 | await serial._port.open({
26 | baudRate: serial._baud
27 | });
28 | serial._textEncoder = new TextEncoderStream();
29 | serial._writableStreamClosed = serial._textEncoder.readable.pipeTo(serial._port.writable);
30 | serial._writer = serial._textEncoder.writable.getWriter();
31 | serial._emit('open', serial._port);
32 | while (serial._port.readable) {
33 | serial._textDecoder = new TextDecoderStream();
34 | serial._readableStreamClosed = serial._port.readable.pipeTo(serial._textDecoder.writable);
35 | serial._reader = serial._textDecoder.readable.getReader();
36 | try {
37 | while (true) {
38 | const {value, done} = await serial._reader.read();
39 | if (done) {
40 | serial._reader.releaseLock();
41 | break;
42 | }
43 | serial._emit('data', value);
44 | }
45 | } catch (e) {
46 | serial._emit('error', e);
47 | }
48 | }
49 | } catch (e) {
50 | serial._emit('error', e);
51 | }
52 | }
53 |
54 | serial.write = async function (text, sendLineBreak = false) {
55 | if (sendLineBreak) text += '\r\n';
56 | try {
57 | await serial._writer.write(text);
58 | } catch (e) {
59 | serial._emit('error', e);
60 | }
61 | }
62 |
63 | serial.close = async function () {
64 | await serial._reader.cancel();
65 | await serial._readableStreamClosed.catch(() => {
66 | /* ignore exceptions */
67 | });
68 | await serial._writer.close();
69 | await serial._writableStreamClosed;
70 | await serial._port.close();
71 | serial._emit('close');
72 | }
73 |
74 | serial.getGrantedPorts = async function () {
75 | return await navigator.serial.getPorts();
76 | }
77 |
78 | serial._emit = function (event, message = null) {
79 | switch (event) {
80 | case 'data':
81 | serial._onRecv(message);
82 | break;
83 | case 'open':
84 | if (serial._onOpen) serial._onOpen(message);
85 | break;
86 | case 'close':
87 | if (serial._onClose) serial._onClose(message);
88 | break;
89 | case 'error':
90 | serial._onError(message);
91 | break;
92 | default:
93 | console.error(`Invalid event[${event}] emitted`);
94 | break;
95 | }
96 | }
97 | }
98 |
99 | module.exports = Serial;
100 |
--------------------------------------------------------------------------------
/libs/tempc.js:
--------------------------------------------------------------------------------
1 | const TEMPERATURE_TYPE = {
2 | Celsius: 'C',
3 | Fahrenheit: 'F',
4 | Kelvin: 'K',
5 | Rankine: 'Ra',
6 | Reaumur: 'Re',
7 | }
8 |
9 | const tempc = (value, from, to) => {
10 | value = Number(value);
11 | switch (from) {
12 | case TEMPERATURE_TYPE.Fahrenheit:
13 | value = (value - 32) / 1.8;
14 | break;
15 | case TEMPERATURE_TYPE.Kelvin:
16 | value = value - 273.15;
17 | break;
18 | case TEMPERATURE_TYPE.Rankine:
19 | value = value / 1.8 - 273.15;
20 | break;
21 | case TEMPERATURE_TYPE.Reaumur:
22 | value = value * 1.25;
23 | break;
24 | }
25 | switch (to) {
26 | case TEMPERATURE_TYPE.Fahrenheit:
27 | value = value * 1.8 + 32
28 | break;
29 | case TEMPERATURE_TYPE.Kelvin:
30 | value = value + 273.15;
31 | break;
32 | case TEMPERATURE_TYPE.Rankine:
33 | value = (value + 273.15) * 1.8;
34 | break;
35 | case TEMPERATURE_TYPE.Reaumur:
36 | value = value / 1.25;
37 | break;
38 | }
39 | return value.toString();
40 | };
41 |
42 | module.exports = {tempc, TEMPERATURE_TYPE};
43 |
--------------------------------------------------------------------------------
/libs/utf8-util.js:
--------------------------------------------------------------------------------
1 | const UTF8Translate = {
2 | Encode: function (pValue) {
3 | return pValue.replace(/[^\u0000-\u00FF]/g, function ($0) {
4 | return escape($0).replace(/(%u)(\w{4})/gi, "$2;")
5 | });
6 | },
7 | Decode: function (pValue) {
8 | return unescape(pValue.replace(//g, '%u').replace(/\\u/g, '%u').replace(/;/g, ''));
9 | }
10 | };
11 |
12 | module.exports = UTF8Translate;
13 |
--------------------------------------------------------------------------------
/libs/vigenereCipher.js:
--------------------------------------------------------------------------------
1 | // 生成加密表
2 | const genVigenereCipherTable = () => {
3 | let res = []
4 | const CharCodeA = 'A'.charCodeAt(0);
5 | for (let i = 0; i < 26; i++) {
6 | let resOneLine = []
7 | for (let j = 0; j < 26; j++) {
8 | let charRes = String.fromCharCode(CharCodeA + (i + j) % 26);
9 | resOneLine = [...resOneLine, charRes]
10 | }
11 | res = [...res, resOneLine]
12 | }
13 | return res
14 | }
15 |
16 | const vigenereCipherTable = genVigenereCipherTable()
17 |
18 | // 原文 + 密钥 => 密文
19 | const encryptVigenere = (values, key) => {
20 | let res = ''
21 | key = key.toUpperCase().replace(/[^A-Z]/, '')
22 | let keyLen = key.length
23 | values = values.toUpperCase()
24 | let count = 0
25 | for (const v of values) {
26 | let resOne = ''
27 | // 非字母不加密
28 | if (!/[A-Z]/.test(v)) {
29 | res += v
30 | continue
31 | }
32 | // v行 key[i]列
33 | let i = v.charCodeAt(0) - 'A'.charCodeAt(0);
34 | let j = key[count % keyLen].charCodeAt(0) - 'A'.charCodeAt(0);
35 | resOne = vigenereCipherTable[i][j]
36 | res += resOne
37 | count++
38 | }
39 | return res
40 | }
41 | // 密文 + 密钥 => 原文
42 | // 本质是求密码表第几行所对应第字
43 | const decryptVigenere = (values, key) => {
44 | let res = ''
45 | key = key.toUpperCase().replace(/[^A-Z]/, '')
46 | let keyLen = key.length
47 | values = values.toUpperCase()
48 | let count = 0
49 | for (const v of values) {
50 | // 非字母不加密
51 | if (!/[A-Z]/.test(v)) {
52 | res += v
53 | continue
54 | }
55 | let j = key[count % keyLen].charCodeAt(0) - 'A'.charCodeAt(0);
56 | // 本质是求第几行
57 | for (let i = 0; i < 26; i++) {
58 | if (vigenereCipherTable[i][j] === v) {
59 | res += String.fromCharCode(i + 'A'.charCodeAt(0));
60 | }
61 | }
62 | count++
63 | }
64 | return res
65 | }
66 |
67 | // let a = encryptVigenere('a;ba', '1b') // b;cb key只有字母部分有效,value非字母部分不加密
68 | // let b = decryptVigenere('b;cb', '1b') // a;ba
69 | module.exports = {encryptVigenere, decryptVigenere}
70 |
--------------------------------------------------------------------------------
/middleware/title-guard.js:
--------------------------------------------------------------------------------
1 | import {getToolByRoute} from "~/libs/common";
2 |
3 | const preferences = {
4 | previousPath: ''
5 | }
6 |
7 | const regexps = {
8 | index: /^index/i,
9 | tools: /^tools/i,
10 | tag: /^tag/i,
11 | settings: /^settings/i,
12 | }
13 |
14 | export default function ({route, store, app}) {
15 | if (route.path !== preferences.previousPath) {
16 | if (regexps.index.test(route.name)) {
17 | store.commit('runtime/setCurrentTitle', '')
18 | } else if (regexps.tools.test(route.name)) {
19 | const universalPath = route.path.replace(/^\/[a-z]{2}\//i, '/')
20 | store.commit('runtime/setCurrentTitle', getToolByRoute(universalPath).title)
21 | } else if (regexps.tag.test(route.name)) {
22 | store.commit('runtime/setCurrentTitle', `tags.${route.params.tag}`)
23 | } else if (regexps.settings.test(route.name)) {
24 | store.commit('runtime/setCurrentTitle', 'page.settings.title')
25 | }
26 |
27 | store.commit('runtime/setShowMiniBar', regexps.tools.test(route.name))
28 | } else {
29 | /* ignore */
30 | }
31 | preferences.previousPath = route.path
32 | }
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ctfever",
3 | "version": "2.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "nuxt",
7 | "build": "nuxt build",
8 | "start": "nuxt start",
9 | "generate": "nuxt generate",
10 | "test": "jest --passWithNoTests"
11 | },
12 | "dependencies": {
13 | "@nuxt/content": "^1.15.1",
14 | "@nuxtjs/axios": "^5.13.6",
15 | "@nuxtjs/i18n": "^7.2.0",
16 | "@nuxtjs/proxy": "^2.1.0",
17 | "@nuxtjs/pwa": "^3.3.5",
18 | "@nuxtjs/robots": "^2.5.0",
19 | "@nuxtjs/sitemap": "^2.4.0",
20 | "@nuxtjs/toast": "^3.3.1",
21 | "@nuxtjs/workbox": "^3.0.0-beta.16",
22 | "ant-design-vue": "^1.7.8",
23 | "bigi": "^1.4.2",
24 | "core-js": "^3.19.3",
25 | "dayjs": "^1.11.2",
26 | "fast-glob": "^3.2.12",
27 | "hi-base32": "^0.5.1",
28 | "hi-base64": "^0.2.1",
29 | "highlight.js": "^11.5.1",
30 | "js-md5": "^0.7.3",
31 | "js-sha1": "^0.6.0",
32 | "js-sha256": "^0.9.0",
33 | "js-sha512": "^0.8.0",
34 | "jsfuck": "^0.4.0",
35 | "jsonpath": "^1.1.1",
36 | "localforage": "^1.10.0",
37 | "lodash": "^4.17.21",
38 | "mousetrap": "^1.6.5",
39 | "nuxt": "^2.15.8",
40 | "nuxt-matomo": "^1.2.4",
41 | "pattern.css": "^1.0.0",
42 | "uuid": "^8.3.2",
43 | "vue": "^2.6.14",
44 | "vue-codemirror": "4.0.6",
45 | "vue-gtag": "^1.16.1",
46 | "vue-js-modal": "^2.0.1",
47 | "vue-server-renderer": "^2.6.14",
48 | "vue-template-compiler": "^2.6.14",
49 | "vuex-along": "^1.2.13",
50 | "webpack": "^4.46.0",
51 | "xmorse": "^1.0.0"
52 | },
53 | "devDependencies": {
54 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
55 | "@iconify/vue2": "^2.1.0",
56 | "@nuxt/postcss8": "^1.1.3",
57 | "@nuxt/types": "~2.15.0",
58 | "@nuxtjs/device": "^2.1.0",
59 | "@tailwindcss/typography": "^0.5.2",
60 | "@vue/test-utils": "^1.3.0",
61 | "autoprefixer": "^10.4.4",
62 | "babel-core": "7.0.0-bridge.0",
63 | "babel-jest": "^27.4.4",
64 | "jest": "^27.4.4",
65 | "postcss": "^8.4.12",
66 | "tailwindcss": "^3.0.23",
67 | "vue-jest": "^3.0.4"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/pages/_.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{ $t('page.notFound.title') }}
7 |
{{ $route.path }}
8 |
9 |
10 |
{{ $t('page.notFound.message') }}
11 |
12 | {{ from === '/' ? $t('page.notFound.btn') : $t('page.notFound.btnPrevious') }}
13 |
14 |
15 |
16 |
17 |
41 |
42 |
45 |
--------------------------------------------------------------------------------
/pages/premium-active.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | CTFever Premium
4 | Coming soon...
5 | Premium 版本提供了除 Basic 版本外的额外高级在线功能,包括但不限于:pyc 在线反编译、在线执行 binwalk、端口分析工具 等。Premium 内容将会在
6 | CTFever Basic 第一个版本开发完毕上线后进入开发流程。Premium 上线初期将会提供免费体验次数。
7 | CTFever Basic 所含内容将永久免费且开源;Premium 计划提供订阅制收费服务。
8 |
9 |
10 |
11 |
25 |
26 |
29 |
--------------------------------------------------------------------------------
/pages/static/pigpen/pigpen-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/pages/static/pigpen/pigpen-regular.woff
--------------------------------------------------------------------------------
/pages/static/pigpen/pigpen-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/pages/static/pigpen/pigpen-regular.woff2
--------------------------------------------------------------------------------
/pages/tag/_tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
56 |
57 |
60 |
--------------------------------------------------------------------------------
/pages/tools/ascii.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
38 |
39 |
40 |
125 |
126 |
129 |
--------------------------------------------------------------------------------
/pages/tools/base-series.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
28 |
82 |
83 |
86 |
--------------------------------------------------------------------------------
/pages/tools/base64-to-image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 |
38 |
39 |
100 |
101 |
104 |
--------------------------------------------------------------------------------
/pages/tools/brain-fuck.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
39 |
40 |
41 |
42 |
43 |
114 |
115 |
118 |
--------------------------------------------------------------------------------
/pages/tools/caesar-cipher.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
29 |
30 |
31 |
32 |
103 |
104 |
107 |
--------------------------------------------------------------------------------
/pages/tools/cloud-shadow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/pages/tools/core-values-cipher.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 | {{ $t('common.btn_encrypt') }}
10 | {{ $t('common.btn_decrypt') }}
11 |
12 | {{
13 | $t('common.btn_clean')
14 | }}
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
58 |
59 |
62 |
--------------------------------------------------------------------------------
/pages/tools/ip-geo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ isLoading ? 'Loading...' : 'Query' }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
74 |
75 |
78 |
--------------------------------------------------------------------------------
/pages/tools/jsfuck.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
28 |
29 |
67 |
68 |
71 |
--------------------------------------------------------------------------------
/pages/tools/message-digest.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
17 |
18 |
125 |
126 |
129 |
--------------------------------------------------------------------------------
/pages/tools/morse-code.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
30 |
31 |
32 |
33 |
34 |
72 |
73 |
76 |
--------------------------------------------------------------------------------
/pages/tools/pigpen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 | {{ rowValue }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{ $t('common.btn_clean') }}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
75 |
76 |
89 |
--------------------------------------------------------------------------------
/pages/tools/pyc-decompiler.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 |
23 |
24 |
25 |
103 |
104 |
107 |
--------------------------------------------------------------------------------
/pages/tools/radix-conversion.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
8 |
10 |
12 |
14 |
15 |
16 |
17 |
18 |
125 |
126 |
129 |
--------------------------------------------------------------------------------
/pages/tools/rail-fence-cipher.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
29 |
30 |
31 |
32 |
82 |
83 |
86 |
--------------------------------------------------------------------------------
/pages/tools/rot-series.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 |
28 |
29 |
30 |
69 |
70 |
73 |
--------------------------------------------------------------------------------
/pages/tools/temperature-conversion.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
96 |
97 |
100 |
--------------------------------------------------------------------------------
/pages/tools/timestamp.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
13 |
14 |
15 | this.isUnixTime = val === 'sec'" v-model="unit"/>
17 |
18 |
19 |
20 | {{ $t('tool.timeStamp.now').toString() }}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
33 |
34 |
35 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
123 |
124 |
127 |
--------------------------------------------------------------------------------
/pages/tools/url-encoding.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
28 |
57 |
58 |
61 |
--------------------------------------------------------------------------------
/pages/tools/utf8-conversion.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 | {{ $t('common.text_encode').toString() }}
10 | {{ $t('common.text_decode').toString() }}
11 |
12 | {{
13 | $t('common.btn_clean').toString()
14 | }}
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
60 |
61 |
64 |
--------------------------------------------------------------------------------
/pages/tools/uuid-generator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ $t('common.btn_generate').toString() }}
9 |
10 | {{ uuidResult }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {{ $t('common.btn_validate').toString() }}
19 | {{ $t('common.btn_parse').toString() }}
20 | {{ $t('common.btn_version').toString() }}
21 |
22 |
23 | {{ validateResult }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
107 |
108 |
111 |
--------------------------------------------------------------------------------
/pages/tools/vigenereCipher.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
34 |
35 |
36 |
37 |
86 |
87 |
90 |
--------------------------------------------------------------------------------
/pages/tools/zero-width-steganography.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
18 |
19 | 编码
20 | 解码
21 |
22 |
23 |
24 |
25 | 下载隐写文本文件
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/plugins/antd-ui.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Antd from 'ant-design-vue/lib'
3 |
4 | Vue.use(Antd)
5 |
--------------------------------------------------------------------------------
/plugins/api.js:
--------------------------------------------------------------------------------
1 | import Gateway from "~/api/gateway";
2 | import Pyc from "~/api/tool/pyc";
3 | import Releases from "~/api/releases";
4 | import PortScan from "~/api/tool/port-scan";
5 | import Binwalk from "~/api/tool/binwalk";
6 | import ZipPe from "~/api/tool/zip-pe";
7 |
8 | export default (context, inject) => {
9 | const factories = {
10 | gateway: Gateway(context.$axios),
11 | releases: Releases(context.$axios),
12 |
13 | tool: {
14 | pyc: Pyc(context.$axios),
15 | portScan: PortScan(context.$axios),
16 | binwalk: Binwalk(context.$axios),
17 | zipUtil: ZipPe(context.$axios)
18 | }
19 | }
20 |
21 | inject('api', factories);
22 | }
23 |
--------------------------------------------------------------------------------
/plugins/g-tag.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import VueGtag from "vue-gtag";
3 |
4 | Vue.use(VueGtag, { config: { id: "G-ZD59S22S8Y" } });
5 |
--------------------------------------------------------------------------------
/plugins/vue-js-modal.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Modal from 'vue-js-modal/dist/index'
3 |
4 | import 'vue-js-modal/dist/styles.css'
5 |
6 | Vue.use(Modal, {
7 | dialog: true
8 | })
9 |
--------------------------------------------------------------------------------
/static/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/static/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2b5797
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/static/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/favicon-32x32.png
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/favicon.ico
--------------------------------------------------------------------------------
/static/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/icon.png
--------------------------------------------------------------------------------
/static/icon.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/static/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/mstile-150x150.png
--------------------------------------------------------------------------------
/static/readme/afdian.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/readme/afdian.jpg
--------------------------------------------------------------------------------
/static/readme/icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/readme/icon_1024.png
--------------------------------------------------------------------------------
/static/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
34 |
--------------------------------------------------------------------------------
/static/screenshots/footer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/screenshots/footer.png
--------------------------------------------------------------------------------
/static/screenshots/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/screenshots/header.png
--------------------------------------------------------------------------------
/static/screenshots/tool_bin_extractor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/screenshots/tool_bin_extractor.png
--------------------------------------------------------------------------------
/static/screenshots/tool_pseudo_zip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/screenshots/tool_pseudo_zip.png
--------------------------------------------------------------------------------
/static/screenshots/tool_pyc_decompiler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UniiemStudio/CTFever/af30c96f2e721c185603dd6ee0b557fdf867e60b/static/screenshots/tool_pyc_decompiler.png
--------------------------------------------------------------------------------
/static/site.webmanifest.bak:
--------------------------------------------------------------------------------
1 | {
2 | "name": "CTFever",
3 | "short_name": "CTFever",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-background-sync.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.backgroundSync=function(t,e,s,i,n){"use strict";try{self["workbox:background-sync:5.1.4"]&&_()}catch(t){}class a{constructor(t){this.t=t,this.s=new n.DBWrapper("workbox-background-sync",3,{onupgradeneeded:this.i})}async pushEntry(t){delete t.id,t.queueName=this.t,await this.s.add("requests",t)}async unshiftEntry(t){const[e]=await this.s.getAllMatching("requests",{count:1});e?t.id=e.id-1:delete t.id,t.queueName=this.t,await this.s.add("requests",t)}async popEntry(){return this.h({direction:"prev"})}async shiftEntry(){return this.h({direction:"next"})}async getAll(){return await this.s.getAllMatching("requests",{index:"queueName",query:IDBKeyRange.only(this.t)})}async deleteEntry(t){await this.s.delete("requests",t)}async h({direction:t}){const[e]=await this.s.getAllMatching("requests",{direction:t,index:"queueName",query:IDBKeyRange.only(this.t),count:1});if(e)return await this.deleteEntry(e.id),e}i(t){const e=t.target.result;t.oldVersion>0&&t.oldVersion<3&&e.objectStoreNames.contains("requests")&&e.deleteObjectStore("requests");e.createObjectStore("requests",{autoIncrement:!0,keyPath:"id"}).createIndex("queueName","queueName",{unique:!1})}}const r=["method","referrer","referrerPolicy","mode","credentials","cache","redirect","integrity","keepalive"];class c{constructor(t){"navigate"===t.mode&&(t.mode="same-origin"),this.u=t}static async fromRequest(t){const e={url:t.url,headers:{}};"GET"!==t.method&&(e.body=await t.clone().arrayBuffer());for(const[s,i]of t.headers.entries())e.headers[s]=i;for(const s of r)void 0!==t[s]&&(e[s]=t[s]);return new c(e)}toObject(){const t=Object.assign({},this.u);return t.headers=Object.assign({},this.u.headers),t.body&&(t.body=t.body.slice(0)),t}toRequest(){return new Request(this.u.url,this.u)}clone(){return new c(this.toObject())}}const h=new Set,u=t=>{const e={request:new c(t.requestData).toRequest(),timestamp:t.timestamp};return t.metadata&&(e.metadata=t.metadata),e};class o{constructor(t,{onSync:s,maxRetentionTime:i}={}){if(this.o=!1,this.q=!1,h.has(t))throw new e.WorkboxError("duplicate-queue-name",{name:t});h.add(t),this.l=t,this.m=s||this.replayRequests,this.p=i||10080,this.g=new a(this.l),this.R()}get name(){return this.l}async pushRequest(t){await this.k(t,"push")}async unshiftRequest(t){await this.k(t,"unshift")}async popRequest(){return this.D("pop")}async shiftRequest(){return this.D("shift")}async getAll(){const t=await this.g.getAll(),e=Date.now(),s=[];for(const i of t){const t=60*this.p*1e3;e-i.timestamp>t?await this.g.deleteEntry(i.id):s.push(u(i))}return s}async k({request:t,metadata:e,timestamp:s=Date.now()},i){const n={requestData:(await c.fromRequest(t.clone())).toObject(),timestamp:s};e&&(n.metadata=e),await this.g[i+"Entry"](n),this.o?this.q=!0:await this.registerSync()}async D(t){const e=Date.now(),s=await this.g[t+"Entry"]();if(s){const i=60*this.p*1e3;return e-s.timestamp>i?this.D(t):u(s)}}async replayRequests(){let t;for(;t=await this.shiftRequest();)try{await fetch(t.request.clone())}catch(s){throw await this.unshiftRequest(t),new e.WorkboxError("queue-replay-failed",{name:this.l})}}async registerSync(){if("sync"in self.registration)try{await self.registration.sync.register("workbox-background-sync:"+this.l)}catch(t){}}R(){"sync"in self.registration?self.addEventListener("sync",t=>{if(t.tag==="workbox-background-sync:"+this.l){const e=async()=>{let e;this.o=!0;try{await this.m({queue:this})}catch(t){throw e=t,e}finally{!this.q||e&&!t.lastChance||await this.registerSync(),this.o=!1,this.q=!1}};t.waitUntil(e())}}):this.m({queue:this})}static get _(){return h}}return t.BackgroundSyncPlugin=class{constructor(t,e){this.fetchDidFail=async({request:t})=>{await this.v.pushRequest({request:t})},this.v=new o(t,e)}},t.Queue=o,t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-broadcast-update.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.broadcastUpdate=function(t,a,o,s){"use strict";try{self["workbox:broadcast-update:5.1.4"]&&_()}catch(t){}const e=(t,a,o)=>!o.some(o=>t.headers.has(o)&&a.headers.has(o))||o.every(o=>{const s=t.headers.has(o)===a.headers.has(o),e=t.headers.get(o)===a.headers.get(o);return s&&e}),n=["content-length","etag","last-modified"],i=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function c(t){return{cacheName:t.cacheName,updatedURL:t.request.url}}class r{constructor({headersToCheck:t,generatePayload:a}={}){this.t=t||n,this.o=a||c}async notifyIfUpdated(t){if(t.oldResponse&&!e(t.oldResponse,t.newResponse,this.t)){const s={type:"CACHE_UPDATED",meta:"workbox-broadcast-update",payload:this.o(t)};if("navigate"===t.request.mode){let s;t.event instanceof FetchEvent&&(s=t.event.resultingClientId);await o.resultingClientExists(s)&&!i||await a.timeout(3500)}const e=await self.clients.matchAll({type:"window"});for(const t of e)t.postMessage(s)}}}return t.BroadcastCacheUpdate=r,t.BroadcastUpdatePlugin=class{constructor(t){this.cacheDidUpdate=async t=>{s.dontWaitFor(this.s.notifyIfUpdated(t))},this.s=new r(t)}},t.responsesAreSame=e,t}({},workbox.core._private,workbox.core._private,workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-cacheable-response.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(s){"use strict";try{self["workbox:cacheable-response:5.1.4"]&&_()}catch(s){}class t{constructor(s={}){this.s=s.statuses,this.t=s.headers}isResponseCacheable(s){let t=!0;return this.s&&(t=this.s.includes(s.status)),this.t&&t&&(t=Object.keys(this.t).some(t=>s.headers.get(t)===this.t[t])),t}}return s.CacheableResponse=t,s.CacheableResponsePlugin=class{constructor(s){this.cacheWillUpdate=async({response:s})=>this.i.isResponseCacheable(s)?s:null,this.i=new t(s)}},s}({});
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-expiration.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.expiration=function(t,e,s,i,a,n,h){"use strict";try{self["workbox:expiration:5.1.4"]&&_()}catch(t){}const r=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class c{constructor(t){this.t=t,this.s=new i.DBWrapper("workbox-expiration",1,{onupgradeneeded:t=>this.i(t)})}i(t){const e=t.target.result.createObjectStore("cache-entries",{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1}),a.deleteDatabase(this.t)}async setTimestamp(t,e){const s={url:t=r(t),timestamp:e,cacheName:this.t,id:this.h(t)};await this.s.put("cache-entries",s)}async getTimestamp(t){return(await this.s.get("cache-entries",this.h(t))).timestamp}async expireEntries(t,e){const s=await this.s.transaction("cache-entries","readwrite",(s,i)=>{const a=s.objectStore("cache-entries").index("timestamp").openCursor(null,"prev"),n=[];let h=0;a.onsuccess=()=>{const s=a.result;if(s){const i=s.value;i.cacheName===this.t&&(t&&i.timestamp=e?n.push(s.value):h++),s.continue()}else i(n)}}),i=[];for(const t of s)await this.s.delete("cache-entries",t.id),i.push(t.url);return i}h(t){return this.t+"|"+r(t)}}class o{constructor(t,e={}){this.o=!1,this.u=!1,this.l=e.maxEntries,this.m=e.maxAgeSeconds,this.t=t,this.p=new c(t)}async expireEntries(){if(this.o)return void(this.u=!0);this.o=!0;const t=this.m?Date.now()-1e3*this.m:0,s=await this.p.expireEntries(t,this.l),i=await self.caches.open(this.t);for(const t of s)await i.delete(t);this.o=!1,this.u&&(this.u=!1,e.dontWaitFor(this.expireEntries()))}async updateTimestamp(t){await this.p.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.m){return await this.p.getTimestamp(t){if(!a)return null;const n=this.k(a),h=this.D(i);e.dontWaitFor(h.expireEntries());const r=h.updateTimestamp(s.url);if(t)try{t.waitUntil(r)}catch(t){}return n?a:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.D(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.N=t,this.m=t.maxAgeSeconds,this.g=new Map,t.purgeOnQuotaError&&h.registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata())}D(t){if(t===n.cacheNames.getRuntimeName())throw new s.WorkboxError("expire-custom-caches-only");let e=this.g.get(t);return e||(e=new o(t,this.N),this.g.set(t,e)),e}k(t){if(!this.m)return!0;const e=this._(t);if(null===e)return!0;return e>=Date.now()-1e3*this.m}_(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.g)await self.caches.delete(t),await e.delete();this.g=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-navigation-preload.dev.js:
--------------------------------------------------------------------------------
1 | this.workbox = this.workbox || {};
2 | this.workbox.navigationPreload = (function (exports, logger_js) {
3 | 'use strict';
4 |
5 | try {
6 | self['workbox:navigation-preload:5.1.4'] && _();
7 | } catch (e) {
8 | }
9 |
10 | /*
11 | Copyright 2018 Google LLC
12 |
13 | Use of this source code is governed by an MIT-style
14 | license that can be found in the LICENSE file or at
15 | https://opensource.org/licenses/MIT.
16 | */
17 | /**
18 | * @return {boolean} Whether or not the current browser supports enabling
19 | * navigation preload.
20 | *
21 | * @memberof module:workbox-navigation-preload
22 | */
23 |
24 | function isSupported() {
25 | return Boolean(self.registration && self.registration.navigationPreload);
26 | }
27 |
28 | /*
29 | Copyright 2018 Google LLC
30 |
31 | Use of this source code is governed by an MIT-style
32 | license that can be found in the LICENSE file or at
33 | https://opensource.org/licenses/MIT.
34 | */
35 | /**
36 | * If the browser supports Navigation Preload, then this will disable it.
37 | *
38 | * @memberof module:workbox-navigation-preload
39 | */
40 |
41 | function disable() {
42 | if (isSupported()) {
43 | self.addEventListener('activate', event => {
44 | event.waitUntil(self.registration.navigationPreload.disable().then(() => {
45 | {
46 | logger_js.logger.log(`Navigation preload is disabled.`);
47 | }
48 | }));
49 | });
50 | } else {
51 | {
52 | logger_js.logger.log(`Navigation preload is not supported in this browser.`);
53 | }
54 | }
55 | }
56 |
57 | /*
58 | Copyright 2018 Google LLC
59 |
60 | Use of this source code is governed by an MIT-style
61 | license that can be found in the LICENSE file or at
62 | https://opensource.org/licenses/MIT.
63 | */
64 | /**
65 | * If the browser supports Navigation Preload, then this will enable it.
66 | *
67 | * @param {string} [headerValue] Optionally, allows developers to
68 | * [override](https://developers.google.com/web/updates/2017/02/navigation-preload#changing_the_header)
69 | * the value of the `Service-Worker-Navigation-Preload` header which will be
70 | * sent to the server when making the navigation request.
71 | *
72 | * @memberof module:workbox-navigation-preload
73 | */
74 |
75 | function enable(headerValue) {
76 | if (isSupported()) {
77 | self.addEventListener('activate', event => {
78 | event.waitUntil(self.registration.navigationPreload.enable().then(() => {
79 | // Defaults to Service-Worker-Navigation-Preload: true if not set.
80 | if (headerValue) {
81 | self.registration.navigationPreload.setHeaderValue(headerValue);
82 | }
83 |
84 | {
85 | logger_js.logger.log(`Navigation preload is enabled.`);
86 | }
87 | }));
88 | });
89 | } else {
90 | {
91 | logger_js.logger.log(`Navigation preload is not supported in this browser.`);
92 | }
93 | }
94 | }
95 |
96 | exports.disable = disable;
97 | exports.enable = enable;
98 | exports.isSupported = isSupported;
99 |
100 | return exports;
101 |
102 | }({}, workbox.core._private));
103 |
104 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-navigation-preload.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self["workbox:navigation-preload:5.1.4"]&&_()}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({});
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-offline-ga.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(t,o,e,n,a,c,r,w,s){"use strict";try{self["workbox:google-analytics:5.1.4"]&&_()}catch(t){}const i=/^\/(\w+\/)?collect/,l=t=>{const o=({url:t})=>"www.google-analytics.com"===t.hostname&&i.test(t.pathname),e=new s.NetworkOnly({plugins:[t]});return[new c.Route(o,e,"GET"),new c.Route(o,e,"POST")]},g=t=>{const o=new w.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.google-analytics.com"===t.hostname&&"/analytics.js"===t.pathname,o,"GET")},m=t=>{const o=new w.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.googletagmanager.com"===t.hostname&&"/gtag/js"===t.pathname,o,"GET")},u=t=>{const o=new w.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.googletagmanager.com"===t.hostname&&"/gtm.js"===t.pathname,o,"GET")};return t.initialize=(t={})=>{const n=e.cacheNames.getGoogleAnalyticsName(t.cacheName),a=new o.BackgroundSyncPlugin("workbox-google-analytics",{maxRetentionTime:2880,onSync:(c=t,async({queue:t})=>{let o;for(;o=await t.shiftRequest();){const{request:e,timestamp:n}=o,a=new URL(e.url);try{const t="POST"===e.method?new URLSearchParams(await e.clone().text()):a.searchParams,o=n-(Number(t.get("qt"))||0),r=Date.now()-o;if(t.set("qt",String(r)),c.parameterOverrides)for(const o of Object.keys(c.parameterOverrides)){const e=c.parameterOverrides[o];t.set(o,e)}"function"==typeof c.hitFilter&&c.hitFilter.call(null,t),await fetch(new Request(a.origin+a.pathname,{body:t.toString(),method:"POST",mode:"cors",credentials:"omit",headers:{"Content-Type":"text/plain"}}))}catch(e){throw await t.unshiftRequest(o),e}}})});var c;const w=[u(n),g(n),m(n),...l(a)],s=new r.Router;for(const t of w)s.registerRoute(t);s.addFetchListener()},t}({},workbox.backgroundSync,workbox.core._private,workbox.core._private,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-range-requests.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.rangeRequests=function(t,e,n){"use strict";try{self["workbox:range-requests:5.1.4"]&&_()}catch(t){}async function r(t,n){try{if(206===n.status)return n;const r=t.headers.get("range");if(!r)throw new e.WorkboxError("no-range-header");const s=function(t){const n=t.trim().toLowerCase();if(!n.startsWith("bytes="))throw new e.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:n});if(n.includes(","))throw new e.WorkboxError("single-range-only",{normalizedRangeHeader:n});const r=/(\d*)-(\d*)/.exec(n);if(!r||!r[1]&&!r[2])throw new e.WorkboxError("invalid-range-values",{normalizedRangeHeader:n});return{start:""===r[1]?void 0:Number(r[1]),end:""===r[2]?void 0:Number(r[2])}}(r),a=await n.blob(),o=function(t,n,r){const s=t.size;if(r&&r>s||n&&n<0)throw new e.WorkboxError("range-not-satisfiable",{size:s,end:r,start:n});let a,o;return void 0!==n&&void 0!==r?(a=n,o=r+1):void 0!==n&&void 0===r?(a=n,o=s):void 0!==r&&void 0===n&&(a=s-r,o=s),{start:a,end:o}}(a,s.start,s.end),i=a.slice(o.start,o.end),d=i.size,u=new Response(i,{status:206,statusText:"Partial Content",headers:n.headers});return u.headers.set("Content-Length",String(d)),u.headers.set("Content-Range",`bytes ${o.start}-${o.end-1}/`+a.size),u}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await r(t,e):e}},t.createPartialResponse=r,t}({},workbox.core._private,workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-routing.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.routing=function(t,e){"use strict";try{self["workbox:routing:5.1.4"]&&_()}catch(t){}const s=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,r="GET"){this.handler=s(e),this.match=t,this.method=r}}class n extends r{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class o{constructor(){this.t=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);const e=new Request(...t);return this.handleRequest({request:e})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const{params:r,route:n}=this.findMatchingRoute({url:s,request:t,event:e});let o,i=n&&n.handler;if(!i&&this.s&&(i=this.s),i){try{o=i.handle({url:s,request:t,event:e,params:r})}catch(t){o=Promise.reject(t)}return o instanceof Promise&&this.o&&(o=o.catch(r=>this.o.handle({url:s,request:t,event:e}))),o}}findMatchingRoute({url:t,request:e,event:s}){const r=this.t.get(e.method)||[];for(const n of r){let r;const o=n.match({url:t,request:e,event:s});if(o)return r=o,(Array.isArray(o)&&0===o.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(r=void 0),{route:n,params:r}}return{}}setDefaultHandler(t){this.s=s(t)}setCatchHandler(t){this.o=s(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new e.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const s=this.t.get(t.method).indexOf(t);if(!(s>-1))throw new e.WorkboxError("unregister-route-route-not-registered");this.t.get(t.method).splice(s,1)}}let i;const u=()=>(i||(i=new o,i.addFetchListener(),i.addCacheListener()),i);return t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super(t=>this.i(t),t),this.u=e,this.h=s}i({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.h)if(t.test(s))return!1;return!!this.u.some(t=>t.test(s))}},t.RegExpRoute=n,t.Route=r,t.Router=o,t.registerRoute=function(t,s,o){let i;if("string"==typeof t){const e=new URL(t,location.href);i=new r(({url:t})=>t.href===e.href,s,o)}else if(t instanceof RegExp)i=new n(t,s,o);else if("function"==typeof t)i=new r(t,s,o);else{if(!(t instanceof r))throw new e.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});i=t}return u().registerRoute(i),i},t.setCatchHandler=function(t){u().setCatchHandler(t)},t.setDefaultHandler=function(t){u().setDefaultHandler(t)},t}({},workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-strategies.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.strategies=function(t,e,s,i,n){"use strict";try{self["workbox:strategies:5.1.4"]&&_()}catch(t){}const r={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null};return t.CacheFirst=class{constructor(t={}){this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){"string"==typeof e&&(e=new Request(e));let i,r=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(!r)try{r=await this.o(e,t)}catch(t){i=t}if(!r)throw new n.WorkboxError("no-response",{url:e.url,error:i});return r}async o(t,e){const n=await i.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s}),r=n.clone(),h=s.cacheWrapper.put({cacheName:this.t,request:t,response:r,event:e,plugins:this.s});if(e)try{e.waitUntil(h)}catch(t){}return n}},t.CacheOnly=class{constructor(t={}){this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],this.h=t.matchOptions}async handle({event:t,request:e}){"string"==typeof e&&(e=new Request(e));const i=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(!i)throw new n.WorkboxError("no-response",{url:e.url});return i}},t.NetworkFirst=class{constructor(t={}){if(this.t=e.cacheNames.getRuntimeName(t.cacheName),t.plugins){const e=t.plugins.some(t=>!!t.cacheWillUpdate);this.s=e?t.plugins:[r,...t.plugins]}else this.s=[r];this.u=t.networkTimeoutSeconds||0,this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){const s=[];"string"==typeof e&&(e=new Request(e));const i=[];let r;if(this.u){const{id:n,promise:h}=this.l({request:e,event:t,logs:s});r=n,i.push(h)}const h=this.p({timeoutId:r,request:e,event:t,logs:s});i.push(h);let o=await Promise.race(i);if(o||(o=await h),!o)throw new n.WorkboxError("no-response",{url:e.url});return o}l({request:t,logs:e,event:s}){let i;return{promise:new Promise(e=>{i=setTimeout(async()=>{e(await this.q({request:t,event:s}))},1e3*this.u)}),id:i}}async p({timeoutId:t,request:e,logs:n,event:r}){let h,o;try{o=await i.fetchWrapper.fetch({request:e,event:r,fetchOptions:this.i,plugins:this.s})}catch(t){h=t}if(t&&clearTimeout(t),h||!o)o=await this.q({request:e,event:r});else{const t=o.clone(),i=s.cacheWrapper.put({cacheName:this.t,request:e,response:t,event:r,plugins:this.s});if(r)try{r.waitUntil(i)}catch(t){}}return o}q({event:t,request:e}){return s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s})}},t.NetworkOnly=class{constructor(t={}){this.s=t.plugins||[],this.i=t.fetchOptions}async handle({event:t,request:e}){let s,r;"string"==typeof e&&(e=new Request(e));try{r=await i.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s})}catch(t){s=t}if(!r)throw new n.WorkboxError("no-response",{url:e.url,error:s});return r}},t.StaleWhileRevalidate=class{constructor(t={}){if(this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],t.plugins){const e=t.plugins.some(t=>!!t.cacheWillUpdate);this.s=e?t.plugins:[r,...t.plugins]}else this.s=[r];this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){"string"==typeof e&&(e=new Request(e));const i=this.o({request:e,event:t});let r,h=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(h){if(t)try{t.waitUntil(i)}catch(r){}}else try{h=await i}catch(t){r=t}if(!h)throw new n.WorkboxError("no-response",{url:e.url,error:r});return h}async o({request:t,event:e}){const n=await i.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s}),r=s.cacheWrapper.put({cacheName:this.t,request:t,response:n.clone(),event:e,plugins:this.s});if(e)try{e.waitUntil(r)}catch(t){}return n}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-streams.prod.js:
--------------------------------------------------------------------------------
1 | this.workbox=this.workbox||{},this.workbox.streams=function(e,n,t){"use strict";try{self["workbox:streams:5.1.4"]&&_()}catch(e){}function s(e){const t=e.map(e=>Promise.resolve(e).then(e=>function(e){return e instanceof Response?e.body.getReader():e instanceof ReadableStream?e.getReader():new Response(e).body.getReader()}(e))),s=new n.Deferred;let r=0;const o=new ReadableStream({pull(e){return t[r].then(e=>e.read()).then(n=>{if(n.done)return r++,r>=t.length?(e.close(),void s.resolve()):this.pull(e);e.enqueue(n.value)}).catch(e=>{throw s.reject(e),e})},cancel(){s.resolve()}});return{done:s.promise,stream:o}}function r(e={}){const n=new Headers(e);return n.has("content-type")||n.set("content-type","text/html"),n}function o(e,n){const{done:t,stream:o}=s(e),c=r(n);return{done:t,response:new Response(o,{headers:c})}}function c(){return t.canConstructReadableStream()}return e.concatenate=s,e.concatenateToResponse=o,e.isSupported=c,e.strategy=function(e,n){return async({event:t,request:s,url:a,params:u})=>{const i=e.map(e=>Promise.resolve(e({event:t,request:s,url:a,params:u})));if(c()){const{done:e,response:s}=o(i,n);return t&&t.waitUntil(e),s}const f=i.map(async e=>{const n=await e;return n instanceof Response?n.blob():new Response(n).blob()}),p=await Promise.all(f),w=r(n);return new Response(new Blob(p),{headers:w})}},e}({},workbox.core._private,workbox.core._private);
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-sw.js:
--------------------------------------------------------------------------------
1 | !function(){"use strict";try{self["workbox:sw:5.1.4"]&&_()}catch(t){}const t={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};self.workbox=new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.s=this.t.debug?"dev":"prod",this.o=!1,new Proxy(this,{get(e,s){if(e[s])return e[s];const o=t[s];return o&&e.loadModule("workbox-"+o),e[s]}})}setConfig(t={}){if(this.o)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.s=this.t.debug?"dev":"prod"}loadModule(t){const e=this.i(t);try{importScripts(e),this.o=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}i(t){if(this.t.modulePathCb)return this.t.modulePathCb(t,this.t.debug);let e=["https://cdn.jsdelivr.net/npm/workbox-cdn@5.1.4/workbox"];const s=`${t}.${this.s}.js`,o=this.t.modulePathPrefix;return o&&(e=o.split("/"),""===e[e.length-1]&&e.splice(e.length-1,1)),e.push(s),e.join("/")}}}();
2 |
3 |
--------------------------------------------------------------------------------
/static/third_party/workbox/workbox-window.prod.mjs:
--------------------------------------------------------------------------------
1 | try {
2 | self["workbox:window:5.1.4"] && _()
3 | } catch (t) {
4 | }
5 |
6 | function t(t, s) {
7 | return new Promise(i => {
8 | const e = new MessageChannel;
9 | e.port1.onmessage = t => {
10 | i(t.data)
11 | }, t.postMessage(s, [e.port2])
12 | })
13 | }
14 |
15 | try {
16 | self["workbox:core:5.1.4"] && _()
17 | } catch (t) {
18 | }
19 |
20 | class s {
21 | constructor() {
22 | this.promise = new Promise((t, s) => {
23 | this.resolve = t, this.reject = s
24 | })
25 | }
26 | }
27 |
28 | function i(t, s) {
29 | const {href: i} = location;
30 | return new URL(t, i).href === new URL(s, i).href
31 | }
32 |
33 | class e {
34 | constructor(t, s) {
35 | this.type = t, Object.assign(this, s)
36 | }
37 | }
38 |
39 | class h extends class {
40 | constructor() {
41 | this.t = new Map
42 | }
43 |
44 | addEventListener(t, s) {
45 | this.s(t).add(s)
46 | }
47 |
48 | removeEventListener(t, s) {
49 | this.s(t).delete(s)
50 | }
51 |
52 | dispatchEvent(t) {
53 | t.target = this;
54 | const s = this.s(t.type);
55 | for (const i of s) i(t)
56 | }
57 |
58 | s(t) {
59 | return this.t.has(t) || this.t.set(t, new Set), this.t.get(t)
60 | }
61 | } {
62 | constructor(t, h = {}) {
63 | super(), this.i = {}, this.h = 0, this.o = new s, this.g = new s, this.l = new s, this.u = 0, this.v = new Set, this.m = () => {
64 | const t = this.p, s = t.installing;
65 | this.h > 0 || !i(s.scriptURL, this.S) || performance.now() > this.u + 6e4 ? (this.L = s, t.removeEventListener("updatefound", this.m)) : (this._ = s, this.v.add(s), this.o.resolve(s)), ++this.h, s.addEventListener("statechange", this.P)
66 | }, this.P = t => {
67 | const s = this.p, i = t.target, {state: h} = i, n = i === this.L, a = n ? "external" : "",
68 | r = {sw: i, originalEvent: t};
69 | !n && this.W && (r.isUpdate = !0), this.dispatchEvent(new e(a + h, r)), "installed" === h ? this.B = self.setTimeout(() => {
70 | "installed" === h && s.waiting === i && this.dispatchEvent(new e(a + "waiting", r))
71 | }, 200) : "activating" === h && (clearTimeout(this.B), n || this.g.resolve(i))
72 | }, this.C = t => {
73 | const s = this._;
74 | s === navigator.serviceWorker.controller && (this.dispatchEvent(new e("controlling", {
75 | sw: s,
76 | originalEvent: t,
77 | isUpdate: this.W
78 | })), this.l.resolve(s))
79 | }, this.R = async t => {
80 | const {data: s, source: i} = t;
81 | await this.getSW(), this.v.has(i) && this.dispatchEvent(new e("message", {data: s, sw: i, originalEvent: t}))
82 | }, this.S = t, this.i = h, navigator.serviceWorker.addEventListener("message", this.R)
83 | }
84 |
85 | async register({immediate: t = !1} = {}) {
86 | t || "complete" === document.readyState || await new Promise(t => window.addEventListener("load", t)), this.W = Boolean(navigator.serviceWorker.controller), this.U = this.M(), this.p = await this.T(), this.U && (this._ = this.U, this.g.resolve(this.U), this.l.resolve(this.U), this.U.addEventListener("statechange", this.P, {once: !0}));
87 | const s = this.p.waiting;
88 | return s && i(s.scriptURL, this.S) && (this._ = s, Promise.resolve().then(() => {
89 | this.dispatchEvent(new e("waiting", {sw: s, wasWaitingBeforeRegister: !0}))
90 | }).then(() => {
91 | })), this._ && (this.o.resolve(this._), this.v.add(this._)), this.p.addEventListener("updatefound", this.m), navigator.serviceWorker.addEventListener("controllerchange", this.C, {once: !0}), this.p
92 | }
93 |
94 | async update() {
95 | this.p && await this.p.update()
96 | }
97 |
98 | get active() {
99 | return this.g.promise
100 | }
101 |
102 | get controlling() {
103 | return this.l.promise
104 | }
105 |
106 | async getSW() {
107 | return void 0 !== this._ ? this._ : this.o.promise
108 | }
109 |
110 | async messageSW(s) {
111 | return t(await this.getSW(), s)
112 | }
113 |
114 | M() {
115 | const t = navigator.serviceWorker.controller;
116 | return t && i(t.scriptURL, this.S) ? t : void 0
117 | }
118 |
119 | async T() {
120 | try {
121 | const t = await navigator.serviceWorker.register(this.S, this.i);
122 | return this.u = performance.now(), t
123 | } catch (t) {
124 | throw t
125 | }
126 | }
127 | }
128 |
129 | export {h as Workbox, t as messageSW};
130 |
131 |
--------------------------------------------------------------------------------
/store/runtime.js:
--------------------------------------------------------------------------------
1 | export const state = () => ({
2 | currentTitle: '',
3 | showMiniBar: false,
4 | })
5 |
6 | export const mutations = {
7 | setCurrentTitle(state, title) {
8 | state.currentTitle = title;
9 | },
10 | setShowMiniBar(state, show) {
11 | state.showMiniBar = show;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/store/settings.js:
--------------------------------------------------------------------------------
1 | import {wrapI18nPath2MetaRoute} from "~/libs/common";
2 |
3 | export const state = () => ({
4 | cloudSync: {
5 | enabled: false,
6 | lastSync: null,
7 | },
8 | markedTool: [],
9 | latest: 0,
10 | settings: {
11 | appearance: 'auto',
12 | },
13 | })
14 |
15 | export const mutations = {
16 | markToolByRoute(state, payload = {route: '', mark: false}) {
17 | payload.route = wrapI18nPath2MetaRoute(payload.route);
18 | if (payload.mark) {
19 | if (state.markedTool.filter(f => f.route === payload.route).length === 0) {
20 | state.markedTool.push(
21 | {
22 | route: payload.route,
23 | create_time: new Date().getTime()
24 | }
25 | )
26 | }
27 | } else {
28 | state.markedTool = state.markedTool.filter(f => f.route !== payload.route);
29 | }
30 | },
31 | setMarkedTools(state, payload) {
32 | state.markedTool = payload;
33 | },
34 | setAppearance(state, appearance) {
35 | if (appearance === 'auto' || appearance === 'light' || appearance === 'dark') {
36 | state.settings.appearance = appearance;
37 | } else {
38 | state.settings.appearance = 'auto';
39 | }
40 | },
41 | setLatest(state, latest) {
42 | state.latest = latest;
43 | },
44 | wipe(state) {
45 | state.cloudSync = {
46 | enabled: false,
47 | lastSync: null,
48 | };
49 | state.markedTool = [];
50 | state.latest = 0;
51 | state.settings = {
52 | appearance: 'auto',
53 | };
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | darkMode: 'class',
3 | content: [
4 | "./components/**/*.{js,vue,ts}",
5 | "./layouts/**/*.vue",
6 | "./pages/**/*.vue",
7 | "./layouts/**/*.vue",
8 | "./plugins/**/*.{js,ts}",
9 | "./nuxt.config.{js,ts}",
10 | "./content/**/*.md"
11 | ],
12 | theme: {
13 | fontFamily: {
14 | 'sans': ["-apple-system", "PT Sans", "PingFang SC", "sans-serif"],
15 | 'mono': ["PT Mono", "monospace"],
16 | },
17 | fallbackFontFamily: {
18 | 'sans': ["-apple-system", "PT Sans", "PingFang SC", "sans-serif"],
19 | 'mono': ["PT Mono", "monospace"],
20 | },
21 | extend: {},
22 | },
23 | plugins: [
24 | require('@tailwindcss/typography'),
25 | ],
26 | }
27 |
--------------------------------------------------------------------------------