Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0fc330d
Add: docker build and push action
SleepyHeeead Apr 28, 2023
e6127c4
Update dependencies
SleepyHeeead Apr 28, 2023
19c529e
Fix: node version
SleepyHeeead Apr 28, 2023
ed6bc50
Update README.md
CareyWang Apr 28, 2023
f297573
update: dependencies
SleepyHeeead Jun 16, 2023
42bb825
add support analyze short url
youshandefeiyang Jul 1, 2023
899d05a
fix: config upload
SleepyHeeead Sep 12, 2023
0870dc4
Merge pull request #140 from youshandefeiyang/master
CareyWang Sep 14, 2023
42063f9
optimize: url parser
SleepyHeeead Sep 14, 2023
b71f372
Update .env
CareyWang Oct 8, 2023
0effaff
Bump @babel/traverse from 7.22.5 to 7.23.2
dependabot[bot] Oct 19, 2023
9a488d3
Bump browserify-sign from 4.2.1 to 4.2.2
dependabot[bot] Oct 27, 2023
98a127b
Merge pull request #150 from CareyWang/dependabot/npm_and_yarn/browse…
CareyWang Oct 27, 2023
96545dc
Bump semver from 5.7.1 to 5.7.2
dependabot[bot] Oct 27, 2023
adba044
Bump word-wrap from 1.2.3 to 1.2.5
dependabot[bot] Oct 27, 2023
7479615
Merge pull request #152 from CareyWang/dependabot/npm_and_yarn/word-w…
CareyWang Oct 27, 2023
c412a02
Merge pull request #151 from CareyWang/dependabot/npm_and_yarn/semver…
CareyWang Oct 27, 2023
918a92a
Merge pull request #149 from CareyWang/dependabot/npm_and_yarn/babel/…
CareyWang Oct 27, 2023
e06b3ed
Update index.html
CareyWang Jan 9, 2024
6428d8a
Update Subconverter.vue
CareyWang Mar 29, 2024
42092c2
Support for more client types.
CareyWang Apr 8, 2024
bf82d18
bugfix
CareyWang Apr 8, 2024
57b62a6
Update Subconverter.vue
CareyWang Apr 26, 2024
4af935f
Fixed compatibility bugs
szkzn Jun 26, 2024
e538e52
Keep style unified
szkzn Jun 26, 2024
0a130fc
Merge pull request #174 from szkzn/Fix-default-null-value
CareyWang Jun 27, 2024
75b8ce7
support expand option
SleepyHeeead Jun 27, 2024
2c6b34a
upgrade dependencies
SleepyHeeead Jun 27, 2024
d292ce0
Support for adding customized conversion parameters
VMatrices Jul 2, 2024
c47bbab
Merge pull request #175 from VMatrices/master
CareyWang Aug 7, 2024
7e8b4e5
Upgrade nodejs version 16 to 20
SleepyHeeead Jan 8, 2025
a8788c1
Update README.md
CareyWang Jan 8, 2025
884eb2e
Update .env
CareyWang Aug 15, 2025
3704cbf
Upgrade Node.js version from 20 to 22
SleepyHeeead Dec 7, 2025
9bcbeee
refactor: 重构 Subconverter 组件为模块化架构
SleepyHeeead Dec 7, 2025
acb6174
Merge branch 'dev'
SleepyHeeead Dec 7, 2025
591467d
docs: 更新 README.md
SleepyHeeead Dec 8, 2025
cec8a87
refactor: 优化 Element UI 按需引入和依赖管理
SleepyHeeead Dec 17, 2025
4c1d17c
refactor: 从 Vue CLI 5 迁移到 Vite 5
SleepyHeeead Dec 17, 2025
803732d
Update Vite config and docs
SleepyHeeead Mar 14, 2026
7c98b0d
fix(subconverter): 修复 notify timer 未清理的内存泄漏
SleepyHeeead Jun 1, 2026
ab24261
docs: update README.md and AGENTS.md
SleepyHeeead Jun 1, 2026
a82675f
chore(deps): 升级依赖修复安全漏洞
SleepyHeeead Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions .env
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
VUE_APP_PROJECT = "https://github.com/CareyWang/sub-web"
VITE_PROJECT = "https://github.com/CareyWang/sub-web"

VUE_APP_BOT_LINK = "https://t.me/CareyWong_bot"
VITE_BOT_LINK = "https://t.me/subconverter_discuss"

VUE_APP_BACKEND_RELEASE = "https://github.com/tindy2013/subconverter/releases"
VITE_BACKEND_RELEASE = "https://github.com/tindy2013/subconverter/actions"

VUE_APP_SUBCONVERTER_REMOTE_CONFIG = "https://raw.githubusercontent.com/tindy2013/subconverter/master/base/config/example_external_config.ini"
VITE_SUBCONVERTER_REMOTE_CONFIG = "https://raw.githubusercontent.com/tindy2013/subconverter/master/base/config/example_external_config.ini"

VITE_SUBCONVERTER_DOC_ADVANCED = "https://github.com/tindy2013/subconverter/blob/master/README-cn.md#%E8%BF%9B%E9%98%B6%E9%93%BE%E6%8E%A5"

# API 后端
VUE_APP_SUBCONVERTER_DEFAULT_BACKEND = "https://api.wcc.best"
VITE_SUBCONVERTER_DEFAULT_BACKEND = "https://api.wcc.best"

# 短链接后端
VUE_APP_MYURLS_DEFAULT_BACKEND = "https://suo.yt"
VITE_MYURLS_API = "https://suosuo.de/short"

# 文本托管后端
VUE_APP_CONFIG_UPLOAD_BACKEND = "https://api.wcc.best"
VITE_CONFIG_UPLOAD_API = "https://oss.wcc.best/upload"

# 页面配置
VUE_APP_USE_STORAGE = true
VUE_APP_CACHE_TTL = 86400
VITE_USE_STORAGE = true
VITE_CACHE_TTL = 86400
6 changes: 4 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ module.exports = {
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'semi': 0
'semi': 0,
'vue/multi-word-component-names': 'off'
},
parserOptions: {
parser: 'babel-eslint'
parser: '@babel/eslint-parser',
requireConfigFile: false
}
}
33 changes: 21 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@ name: Build

on:
push:
branches: [ master ]
branches: [ master, dev ]
pull_request:
branches: [ master, dev ]

jobs:

build:
name: Build
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12.x]
node-version: [22.x]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- name: Get dependencies and build
run: |
yarn install
yarn build
- name: Upload
uses: actions/upload-artifact@v1
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run linter
run: yarn lint

- name: Build
run: yarn build

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
name: dist-${{ matrix.node-version }}
path: dist/
retention-days: 7
31 changes: 31 additions & 0 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Build and Push Multi-Arch Docker Image

on:
push:
branches:
- master

jobs:
build-and-push:
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@master

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: careywong/subweb:latest
199 changes: 199 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# AGENTS GUIDE

Vue 2.7 + Vite 8 SPA with Element UI. Keep changes small, follow existing patterns, avoid refactors during fixes.

## Quick Facts

- Framework: Vue 2.7 (Options API)
- Build tool: Vite 8
- UI: Element UI 2
- Router: Vue Router 3 (history mode, base from `import.meta.env.BASE_URL`)
- Node: 22.x
- No automated tests currently

## Commands

| Command | Description |
|---------|-------------|
| `yarn install` | Install dependencies |
| `yarn dev` | Start dev server (host: 0.0.0.0) |
| `yarn build` | Production build |
| `yarn preview` | Preview production build locally |
| `yarn lint` | ESLint check |

## CI / Workflows

- **build.yml**: triggers on push/PR to `master` and `dev` — runs `yarn install --frozen-lockfile` + `yarn lint` + `yarn build`, then uploads `dist/` as artifact (7-day retention)
- **docker-build-push.yml**: triggers on push to `master` — builds and pushes multi-arch image (`linux/amd64`, `linux/arm64`) to `careywong/subweb:latest`

## Repository Layout

```
src/
├── main.js # App bootstrap, plugin registration, Vue mount
├── App.vue
├── router/index.js # Vue Router (history mode)
├── views/Subconverter.vue # Main page
├── components/
│ ├── ConfigUploadDialog.vue # Config upload dialog
│ ├── UrlParseDialog.vue # URL parse dialog
│ └── SvgIcon/index.vue # SVG icon wrapper component
├── composables/
│ ├── useSubscription.js # URL building logic (makeUrl, buildBaseUrl, buildAdvancedParams)
│ ├── useSubscriptionForm.js # Reactive form state + addCustomParam + saveSubUrl
│ └── useUrlParser.js # Short-link expansion + URL-to-form parser (analyzeUrl, parseUrl)
├── services/
│ ├── backendService.js # BackendService.getBackendVersion()
│ ├── shortUrlService.js # ShortUrlService.generateShortUrl()
│ └── configUploadService.js # ConfigUploadService.uploadConfig(), handleUploadSuccess()
├── config/
│ ├── constants.js # CONSTANTS (env-backed, DEFAULT_CLIENT_TYPE='clash')
│ ├── client-types.js # CLIENT_TYPES map (display label → target value)
│ └── remote-configs.js # REMOTE_CONFIGS grouped options array
├── utils/
│ ├── storage.js # getLocalStorageItem / setLocalStorageItem (TTL-based)
│ ├── validators.js # validateSubUrl → { valid, message } | validateForm → boolean
│ ├── formatters.js # formatVersion, formatErrorMessage, processSubUrl
│ └── search.js # Backend autocomplete search helper
├── plugins/ # Vue plugin registrations (element-ui, clipboard, axios, device)
└── icons/
├── index.js # Registers SVG sprite
└── svg/ # SVG source files (e.g., github.svg)
services/ # Docker Compose stack (subweb + myurls + redis)
```

## Key Modules

### `src/config/constants.js`
All values read from `import.meta.env` with `VITE_` prefix. Key constants:
- `DEFAULT_BACKEND` — appends `/sub?` to `VITE_SUBCONVERTER_DEFAULT_BACKEND`
- `DEFAULT_CLIENT_TYPE` — hardcoded `'clash'`
- `SHORT_URL_API`, `CONFIG_UPLOAD_API`, `PROJECT`, `BOT_LINK`, etc.

### `src/composables/useSubscriptionForm.js`
Returns plain object merged into `data()` via spread. Form fields include: `sourceSubUrl`, `clientType`, `customBackend`, `remoteConfig`, `emoji`, `nodeList`, `sort`, `udp`, `tfo`, `scv`, `fdn`, `expand`, `appendType`, `insert`, `new_name`, `tpl.surge.doh`, `tpl.clash.doh`. Default mode is advanced (`advanced: "2"`).

### `src/composables/useSubscription.js`
`makeUrl(form, advanced, processedSubUrl, currentBackend, customParams, needUdp)` — returns empty string on validation failure, otherwise builds full query string. Advanced mode adds remote config, include/exclude, filename, UDP, template, and custom params.

### `src/composables/useUrlParser.js`
`analyzeUrl(url)` — if URL contains `"target"`, returns as-is; otherwise fetches and returns `response.url` (short-link expansion, requires CORS on short-link service).
`parseUrl(url, form, customParams, onSuccess, onError)` — parses all query params back into form fields; unknown params become `customParams` entries.

### `src/services/`
All service classes are static methods. They take `$axios` as first argument (injected via plugin). Silent failures are acceptable for `getBackendVersion`. Upload response shape: `{ code: 0, data: { url }, msg }`. Short URL response shape: `{ Code: 1, ShortUrl, Message }`.

### `src/utils/storage.js`
TTL stored inside the JSON value as `{ setTime, ttl, expire, value }`. `expire` checked on every read; expired entries are removed automatically. TTL value comes from `VITE_CACHE_TTL` env var.

## Code Style

- Indentation: 2 spaces
- Quotes: single quotes preferred
- Semicolons: none (`semi: 0`)
- Vue component names: single-word allowed (`vue/multi-word-component-names: off`)
- `no-console` / `no-debugger`: error in production, off in dev
- ESLint extends: `plugin:vue/essential`, `eslint:recommended`
- Parser: `@babel/eslint-parser` with `requireConfigFile: false`

## Imports & Modules

- ES modules (`import`/`export`) throughout
- Absolute alias `@` maps to `src/` (see `vite.config.js`)
- Import order: core libs → local config/utils → services → components
- Dynamic imports only for route lazy-load

## Vue Patterns

- Options API everywhere; do not introduce Composition API
- Component structure: `<template>`, `<script>`, `<style>`
- Reactive state in `data()`; derived state in `computed`
- Composables spread via `...useSubscription()` / `...useUrlParser()` in `methods`
- `useSubscriptionForm()` spread via `...subscriptionForm` in `data()`

## Icons

- SVG sprites via `vite-plugin-svg-icons`; icon dirs: `src/icons/svg`
- Usage: `<svg-icon icon-class="name" />`
- Symbol ID format: `icon-[name]`

## Environment Variables

- All env vars use `VITE_` prefix; access via `import.meta.env`
- Do not commit `.env.local`, `.env.*.local`
- Constants centralised in `src/config/constants.js` — do not scatter `import.meta.env` calls

## Validation

- Use `src/utils/validators.js` for user-facing checks
- `validateSubUrl` returns `{ valid, message }`; `validateForm` returns boolean
- Do not throw for validation flow

## Error Handling

- UI errors via `this.$message.*` or `this.$notify`
- Silent failures acceptable only when UX demands it (e.g., backend version fetch)
- Use `formatErrorMessage` from `src/utils/formatters.js` for consistent error strings

## Docker

- Base images: `node:22-alpine` (build), `nginx:1.24-alpine` (runtime)
- Build: `yarn install && yarn build`, output copied to `/usr/share/nginx/html`
- Services compose stack in `services/` includes myurls + Redis

## Git Hygiene

- Do not commit `dist/`, `node_modules/`, `.env.local`, `.env.*.local`
- Avoid adding generated files

## Frontend Safety

- Avoid inline styles unless already present in nearby code
- Prefer Element UI components and existing patterns
- Keep UI message strings consistent (mostly Chinese)

## Performance

- Do not introduce heavy dependencies; prefer existing utilities
- Keep all network calls in `src/services/`

## Example Patterns

```js
// Route lazy-load
component: () => import('../views/Subconverter.vue')

// Service class
export class BackendService {
static async getBackendVersion($axios) { ... }
}

// Composable (Options API style)
export function useSubscription() {
return { makeUrl, buildBaseUrl, buildAdvancedParams }
}

// Spread composable into methods
methods: {
...useSubscription(),
...useUrlParser()
}

// Spread form state into data()
data() {
return { ...useSubscriptionForm(), otherField: '' }
}
```

## Suggested Manual Checks

- `yarn lint`
- `yarn build`
- Run `yarn dev` and smoke the main screen

## Notes for Agents

- Follow existing patterns; minimise scope
- No large refactors unless explicitly requested
- Do not introduce TypeScript or new tooling without approval
- No test runner configured; if added, document the single-test command here
11 changes: 3 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
# ---- Dependencies ----
FROM node:16-alpine AS dependencies
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json ./
COPY . .
RUN yarn install

# ---- Build ----
FROM dependencies AS build
WORKDIR /app
COPY . /app
RUN yarn build

FROM nginx:1.16-alpine
FROM nginx:1.24-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD [ "nginx", "-g", "daemon off;" ]
Loading