Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba8a221833 | ||
|
|
3f73cd9443 | ||
|
|
7a0a9dbd92 | ||
|
|
441c4fa6a9 | ||
|
|
a16169f69d | ||
|
|
da42676656 | ||
|
|
f3a42e892f | ||
|
|
6a53597485 | ||
|
|
60336e567d | ||
|
|
8918c62ffd | ||
|
|
362fc19cd8 | ||
|
|
1b938752ff | ||
|
|
835a886dc8 | ||
|
|
f390b57fe7 | ||
|
|
6d21a0f204 | ||
|
|
a1d0c37828 | ||
|
|
763fbc417e | ||
|
|
776d5e66fe | ||
|
|
10c0975ce0 | ||
|
|
7286642fc9 | ||
|
|
8db4fb3524 | ||
|
|
08be04979d | ||
|
|
d7ea9ba43f | ||
|
|
bad248534c | ||
|
|
f56ac4eabc | ||
|
|
bbe2e8416c | ||
|
|
d44e79b1f4 | ||
|
|
e816dd686c | ||
|
|
c9ea8f6162 | ||
|
|
87102cd3b0 | ||
|
|
8b6bab828d | ||
|
|
9fb658599a | ||
|
|
f8c33003a8 | ||
|
|
b97136b83f | ||
|
|
df3e983e7f | ||
|
|
1b878fb3cc | ||
|
|
aa87ab94f5 | ||
|
|
4e9afd9761 | ||
|
|
2b7d0ecb72 | ||
|
|
603c7d9439 | ||
|
|
fb93231b35 | ||
|
|
fed83263e2 | ||
|
|
9dc2d83486 | ||
|
|
d918caaf81 | ||
|
|
d44212bd90 | ||
|
|
37d4ca76fa | ||
|
|
8cc39f322c | ||
|
|
2b4e80651a | ||
|
|
415fa0f2e3 | ||
|
|
91809002d7 | ||
|
|
dcaad1b2d5 | ||
|
|
5e635bba68 | ||
|
|
d136d086eb | ||
|
|
9fe765f874 | ||
|
|
72317ead34 | ||
|
|
75c7914fb1 | ||
|
|
863b4deec0 | ||
|
|
6e6fd8d587 | ||
|
|
1608911328 | ||
|
|
87a62961b6 | ||
|
|
e6a9748e70 | ||
|
|
0f35532f0b | ||
|
|
d21e47f881 | ||
|
|
8d11a239fa |
No files matched your search
@@ -2,6 +2,7 @@ version: 2.1
|
||||
|
||||
orbs:
|
||||
node: circleci/node@5.0.3
|
||||
codecov: codecov/codecov@3.3.0
|
||||
|
||||
executors:
|
||||
base:
|
||||
@@ -9,7 +10,7 @@ executors:
|
||||
- image: cimg/base:stable
|
||||
node:
|
||||
docker:
|
||||
- image: 'cimg/node:18.17.1'
|
||||
- image: 'cimg/node:18.19.0'
|
||||
go:
|
||||
docker:
|
||||
- image: cimg/go:1.21.3
|
||||
@@ -18,9 +19,6 @@ parameters:
|
||||
trigger-app:
|
||||
type: boolean
|
||||
default: false
|
||||
trigger-ui:
|
||||
type: boolean
|
||||
default: false
|
||||
run-them-all:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -52,28 +50,12 @@ jobs:
|
||||
name: Run tests
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports
|
||||
gotestsum --junitfile /tmp/test-reports/unit-tests.xml
|
||||
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -coverprofile=/tmp/test-reports/coverage.out ./...
|
||||
- store_test_results:
|
||||
path: /tmp/test-reports
|
||||
|
||||
## Frontend
|
||||
test_ui:
|
||||
executor: node
|
||||
steps:
|
||||
- node/install
|
||||
- checkout
|
||||
- run:
|
||||
name: Print the node version
|
||||
command: node -v
|
||||
- run:
|
||||
name: Install pnpm
|
||||
command: npm i -g pnpm@8.7.0
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm --dir ./ui install --frozen-lockfile
|
||||
- run:
|
||||
name: Build Artalk
|
||||
command: pnpm --dir ./ui build:all
|
||||
- codecov/upload:
|
||||
flags: go
|
||||
file: /tmp/test-reports/coverage.out
|
||||
|
||||
all_projects:
|
||||
executor: base
|
||||
@@ -91,14 +73,6 @@ workflows:
|
||||
jobs:
|
||||
- test_app
|
||||
|
||||
build-ui:
|
||||
when:
|
||||
or:
|
||||
- << pipeline.parameters.trigger-ui >>
|
||||
- << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
- test_ui
|
||||
|
||||
build-shared-other:
|
||||
when: << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
|
||||
@@ -17,7 +17,6 @@ workflows:
|
||||
mapping: |
|
||||
(cmd|internal|server|pkg|test|conf)/.* trigger-app true
|
||||
main.go|go.mod|go.sum trigger-app true
|
||||
(ui)/.* trigger-ui true
|
||||
base-revision: master # git branch name
|
||||
config-path: .circleci/conditional_config.yml
|
||||
filters:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Test Frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ui/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ui/**'
|
||||
|
||||
jobs:
|
||||
test_ui:
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./ui
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
cache-dependency-path: './ui/pnpm-lock.yaml'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm --dir ./ui install --frozen-lockfile
|
||||
|
||||
- name: Package Build Test
|
||||
run: pnpm build:all
|
||||
|
||||
# TODO: Add unit test
|
||||
# - name: Unit test
|
||||
# run: pnpm test
|
||||
@@ -65,3 +65,6 @@ node_modules
|
||||
# always compile latest version
|
||||
/public/sidebar
|
||||
/public/dist
|
||||
|
||||
playwright-report
|
||||
test-results
|
||||
Vendored
+2
-1
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"eslint.workingDirectories": ["./ui"],
|
||||
"cSpell.words": [
|
||||
"akismet",
|
||||
"artalk",
|
||||
@@ -16,4 +17,4 @@
|
||||
"sqlite",
|
||||
"pkged"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,70 @@
|
||||
|
||||
## [v2.7.1](https://github.com/ArtalkJS/Artalk/compare/v2.7.0...v2.7.1) (2023-12-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** generate source maps to ease debugging
|
||||
* **cout-widget:** `loadCountWidget` issue
|
||||
* **ui:** static method export issue in JS module ([#669](https://github.com/ArtalkJS/Artalk/issues/669))
|
||||
|
||||
### Documentation
|
||||
|
||||
* **i18n:** add translation of update notice
|
||||
|
||||
|
||||
## [v2.7.0](https://github.com/ArtalkJS/Artalk/compare/v2.6.4...v2.7.0) (2023-12-17)
|
||||
|
||||
### Features
|
||||
|
||||
* **ui:** support `scrollRelativeTo` config option
|
||||
* **ui/test:** add end-to-end (e2e) testing using Playwright
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api/img-upload:** `public_path` config enables the use of full url ([#659](https://github.com/ArtalkJS/Artalk/issues/659)) ([#664](https://github.com/ArtalkJS/Artalk/issues/664))
|
||||
* **lifecycle:** create multi-instances at the same time ([#660](https://github.com/ArtalkJS/Artalk/issues/660)) ([#663](https://github.com/ArtalkJS/Artalk/issues/663))
|
||||
* **notify_pusher:** panic when admin ids array is empty ([#634](https://github.com/ArtalkJS/Artalk/issues/634))
|
||||
* **style/list:** replace `float` to `text-align` in footer part ([#619](https://github.com/ArtalkJS/Artalk/issues/619))
|
||||
* **ui:** scroll to the center of the viewport issue
|
||||
* **ui/conf:** sanitize `noComment` conf option for security ([#624](https://github.com/ArtalkJS/Artalk/issues/624))
|
||||
* **ui/dark-mode:** `setDarkMode` cannot save to instance config ([#661](https://github.com/ArtalkJS/Artalk/issues/661))
|
||||
* **ui/editor:** fix position of the comment box when replying ([#643](https://github.com/ArtalkJS/Artalk/issues/643)) ([#648](https://github.com/ArtalkJS/Artalk/issues/648))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **cache:** implement GC feature for simple_cache pkg ([#656](https://github.com/ArtalkJS/Artalk/issues/656))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **editor:** refactor editor plugin manager ([#609](https://github.com/ArtalkJS/Artalk/issues/609))
|
||||
* **editor:** refactor plug kit and events ([#613](https://github.com/ArtalkJS/Artalk/issues/613))
|
||||
* **ui:** reduce coupling with `ContextApi`
|
||||
* **ui:** separate standalone admin-only-elem checker
|
||||
* **ui/api:** loose coupling between `Api` and `Context`
|
||||
* **ui/conf:** loose coupling between config and list fetch
|
||||
* **ui/event:** refactor core event manager ([#611](https://github.com/ArtalkJS/Artalk/issues/611))
|
||||
* **ui/layer:** better layer implements and independence ([#662](https://github.com/ArtalkJS/Artalk/issues/662))
|
||||
* **ui/list:** separate list into standalone components ([#618](https://github.com/ArtalkJS/Artalk/issues/618))
|
||||
* **ui/marked:** separate markdown related codes
|
||||
* **ui/marked:** loose coupling with `marked` func
|
||||
* **ui/plugin:** further divide functionality into plugins ([#615](https://github.com/ArtalkJS/Artalk/issues/615))
|
||||
* **ui/stat:** losing coupling of `CountWidget`
|
||||
* **ui/types:** better `ArtalkType` import and export ([#620](https://github.com/ArtalkJS/Artalk/issues/620))
|
||||
|
||||
### Documentation
|
||||
|
||||
* revise and add more examples
|
||||
* provide clearer and more detailed instructions in CONTRIBUTING.md
|
||||
* **conf:** disable `frontend.uaBadge` config option by default
|
||||
* **deploy:** add deployment guide for render.com ([#649](https://github.com/ArtalkJS/Artalk/issues/649))
|
||||
* **import:** update the examples in import-framework.md ([#665](https://github.com/ArtalkJS/Artalk/issues/665))
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
|
||||
The following top-level functions exported by the 'artalk' npm package have been deprecated: `Artalk.update`, `Artalk.reload`, and `Artalk.destroy`. These methods now require invocation on an instance created by either `Artalk.init` or `new Artalk`. Please utilize instance-level methods instead, such as `artalkInstance.update`. For more information, refer to [the documentation](https://artalk.js.org/guide/frontend/import-framework.html). The update was implemented to enable the concurrent creation of multiple instances, adapting to situations where Vue components are simultaneously referenced across various pages. Initially, only a singular instance was permitted to mitigate memory leak concerns. However, this proved limiting for scenarios involving the caching of multiple component instances through 'keep-alive.' To better suit intricate SPA application needs, the choice was made to permit the creation of multiple independent instances. It's crucial to remember to manually invoke the `artalk.destroy` method when releasing components to avoid memory leaks (Issue [#660](https://github.com/ArtalkJS/Artalk/issues/660)).
|
||||
|
||||
|
||||
## [v2.6.4](https://github.com/ArtalkJS/Artalk/compare/v2.6.3...v2.6.4) (2023-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
+11
-11
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
@@ -59,7 +59,7 @@ representative at an online or offline event.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at artalkjs@gmail.com.
|
||||
reported to the community leaders responsible for enforcement at <artalkjs@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
+38
-11
@@ -6,7 +6,7 @@ This guide is for developers who want to contribute to the project.
|
||||
|
||||
To develop Artalk, including the frontend and backend, you need to install the following tools:
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) (>= 16.0.0)
|
||||
- [Node.js](https://nodejs.org/en/) (>= 18.12.0)
|
||||
- [Go](https://golang.org/) (>= 1.21)
|
||||
- [Docker](https://www.docker.com/) (>= 20.10.0) (optional)
|
||||
- [Docker Compose](https://docs.docker.com/compose/) (>= 1.29.0) (optional)
|
||||
@@ -29,22 +29,41 @@ Enter the directory:
|
||||
cd Artalk
|
||||
```
|
||||
|
||||
### Build frontend and backend
|
||||
### Build Backend
|
||||
|
||||
First, we need to install the dependencies for backend written in Go. Simply run the following command:
|
||||
First, you need to install the dependencies for the backend written in Go. Simply run the `make install` command to install the dependencies.
|
||||
|
||||
```sh
|
||||
make build-debug
|
||||
```
|
||||
Then, run the `make dev` to build and run `./bin/artalk`, and you can pass startup parameters to the program using `ARGS="version" make dev`.
|
||||
|
||||
This will build both the frontend and backend, with debugging symbols.
|
||||
This will build the backend with debugging symbols. The binary file will be placed under the `./bin` directory.
|
||||
|
||||
- **Frontend** will be built under `./ui/packages/artalk` and copied to `./public` directory.
|
||||
- **Backend** will be built under `./bin` directory.
|
||||
The backend program will run by default on port `23366`. You can access it through a browser at `http://localhost:23366`. It's recommended not to change this port number for testing the backend program.
|
||||
|
||||
### Optional: Use one-key script to run a demo site
|
||||
### Build Frontend
|
||||
|
||||
If you want to run a demo site, you can use the following command:
|
||||
First, you need to install the dependencies for the frontend. Simply run the `cd ui && pnpm install` command to install the dependencies.
|
||||
|
||||
Then, run the `cd ui && pnpm dev` to build and run the frontend, and you can pass startup parameters to the program using `ARGS="--port 5173" pnpm dev`.
|
||||
|
||||
The frontend program will run by default on port `5173`, and you can access it in a browser at `http://localhost:5173`. The frontend testing client will, by default, request the backend on port `23366`, so it's essential to keep the backend on this port.
|
||||
|
||||
The frontend program is divided into the main program and a sidebar program, with the sidebar program running on a separate port, which is `23367`.
|
||||
|
||||
### Development Workflow
|
||||
|
||||
In most cases, to set up a complete development instance, you need to navigate to the Artalk Repo directory and then execute `make dev`. This will run the backend on port `23366`. Then, execute `cd ui && pnpm dev`, which will run the frontend on port `5173`. You can optionally execute `pnpm dev:sidebar` to run the sidebar frontend on port `23367`. For frontend development, you need to access `http://localhost:5173` in your browser to perform your development and testing.
|
||||
|
||||
When you make changes to the frontend code, you can build the complete frontend program using `cd ui && pnpm build:all`. The JavaScript and CSS code can be found in `ui/packages/artalk/dist`.
|
||||
|
||||
When you make changes to the backend code, running `make all` will build the complete backend program. Note that since the backend program also embeds the frontend code, the `scripts/build-frontend.sh` script will run during backend program building, which includes the embedded frontend main program and sidebar frontend program. If you are interested, you can explore the complete frontend build process in the `Makefile` code.
|
||||
|
||||
Additionally, there is automated CI on GitHub for building. You can find the relevant code in the `.github/workflows` directory.
|
||||
|
||||
### Optional: Use a One-Key Script to Run a Demo Site
|
||||
|
||||
When you access `http://localhost:5173` during frontend development, you will get a minimalist interface that only contains the Artalk program interface and not a real blog environment. If you want to make testing closer to a real environment, you can create a demo blog site using the following steps.
|
||||
|
||||
Run the following command:
|
||||
|
||||
```sh
|
||||
./scripts/setup-example-site.sh
|
||||
@@ -70,6 +89,14 @@ email: "admin@test.com"
|
||||
password: "admin"
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The backend Go program can be tested by running `make test` for unit testing. The test results will be outputted in the terminal. You can also execute `test-coverage` to check the code test coverage.
|
||||
|
||||
Frontend testing is conducted using Playwright for end-to-end (E2E) testing. To start the E2E testing, run `make test-frontend-e2e`.
|
||||
|
||||
Both frontend and backend testing are automated and will be performed during Git pull requests and as part of the build process using CircleCI or GitHub Actions.
|
||||
|
||||
## Project Structure
|
||||
|
||||
Artalk is a monorepo project, which means all the source code is in the same repository. However, the frontend and backend are separated. The frontend part is located in `./ui` directory.
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ WORKDIR /source
|
||||
# install tools
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache make git gcc musl-dev nodejs bash npm\
|
||||
&& npm install -g pnpm@8.7.0
|
||||
&& npm install -g pnpm@8.12.1
|
||||
|
||||
# download go deps
|
||||
# (cache by separating the downloading of deps)
|
||||
|
||||
@@ -61,6 +61,9 @@ docker-build:
|
||||
docker-push:
|
||||
./scripts/docker-build.sh --push
|
||||
|
||||
test-frontend-e2e:
|
||||
./scripts/frontend-e2e-test.sh $(if $(REPORT), --show-report)
|
||||
|
||||
.PHONY: all install build build-frontend build-debug \
|
||||
dev test test-coverage test-coverage-html update-i18n \
|
||||
docker-build docker-push;
|
||||
docker-build docker-push test-frontend-e2e;
|
||||
+4
-4
@@ -4,12 +4,12 @@
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
|
||||
> 🌌 A Self-hosted comment system
|
||||
> 🌌 A self-hosted comment system
|
||||
|
||||
[简体中文](./README.md) / [Documentation](https://artalk.js.org) / [Releases](https://github.com/ArtalkJS/Artalk/releases) / [Artalk](https://github.com/ArtalkJS/Artalk)
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://hub.docker.com/r/artalk/artalk-go)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
[](https://goreportcard.com/report/github.com/ArtalkJS/Artalk)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
[](https://codecov.io/gh/ArtalkJS/Artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
|
||||
> 🌌 Golang 自托管评论系统
|
||||
|
||||
@@ -53,13 +55,19 @@
|
||||
- 支持 Latex:提供集成 Katex 插件
|
||||
- 使用 [Vite](https://github.com/vitejs/vite):属于开发者的极致体验
|
||||
|
||||
更多内容请查看:[“**介绍文档**”](https://artalk.js.org/guide/intro.html)
|
||||
更多内容请查看:[**介绍文档**](https://artalk.js.org/guide/intro.html)
|
||||
|
||||
## 食用方针
|
||||
## 安装
|
||||
|
||||
前往:[“**部署文档**”](https://artalk.js.org/guide/deploy.html)
|
||||
参考:[**部署文档**](https://artalk.js.org/guide/deploy.html)
|
||||
|
||||
```bash
|
||||
npm install artalk
|
||||
```
|
||||
|
||||
```ts
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
site: 'Artalk 的博客',
|
||||
@@ -74,6 +82,7 @@ docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
--restart=always \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
@@ -89,6 +98,7 @@ services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
@@ -119,7 +129,7 @@ see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
感谢社区提供的帮助与反馈,若有好的建议与意见欢迎前往 [ISSUES](https://github.com/ArtalkJS/Artalk/issues) 随时告知。
|
||||
|
||||
## TODOs
|
||||
## TODOs
|
||||
|
||||
- [x] Golang 后端
|
||||
- [x] 多数据库支持
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ var Banner = `
|
||||
|
||||
Artalk (` + Version + `)
|
||||
|
||||
-> A Selfhosted Comment System.
|
||||
-> A Self-hosted Comment System.
|
||||
-> https://artalk.js.org
|
||||
`
|
||||
|
||||
@@ -50,7 +50,7 @@ func New() *ArtalkCmd {
|
||||
cmd := &ArtalkCmd{
|
||||
RootCmd: &cobra.Command{
|
||||
Use: "artalk",
|
||||
Short: "Artalk: Your self-hosted comment system",
|
||||
Short: "Artalk: A self-hosted comment system",
|
||||
Long: Banner,
|
||||
Version: Version,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
@@ -145,7 +145,7 @@ frontend:
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
vote: true
|
||||
voteDown: false
|
||||
uaBadge: true
|
||||
uaBadge: false
|
||||
listSort: true
|
||||
pvEl: "#ArtalkPV"
|
||||
countEl: "#ArtalkCount"
|
||||
|
||||
@@ -284,7 +284,7 @@ frontend:
|
||||
# Dislike button
|
||||
voteDown: false
|
||||
# User UA badge
|
||||
uaBadge: true
|
||||
uaBadge: false
|
||||
# Comment sorting
|
||||
listSort: true
|
||||
# Page PV binding element
|
||||
|
||||
@@ -291,7 +291,7 @@ frontend:
|
||||
# 反对按钮
|
||||
voteDown: false
|
||||
# 用户 UA 徽标
|
||||
uaBadge: true
|
||||
uaBadge: false
|
||||
# 评论排序功能
|
||||
listSort: true
|
||||
# 页面 PV 绑定元素
|
||||
|
||||
@@ -50,7 +50,8 @@ export default defineConfig({
|
||||
en: {
|
||||
label: 'English',
|
||||
lang: 'en',
|
||||
link: '/en/'
|
||||
link: 'https://artalk-js-org.translate.goog/guide/intro?_x_tr_sl=auto&_x_tr_tl=en-US&_x_tr_hl=en-US&_x_tr_pto=wapp'
|
||||
// link: '/en/'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -94,6 +95,7 @@ export default defineConfig({
|
||||
{ text: '精简版本', link: '/guide/frontend/artalk-lite.md' },
|
||||
{ text: '置入博客', link: '/guide/frontend/import-blog.md' },
|
||||
{ text: '置入框架', link: '/guide/frontend/import-framework.md' },
|
||||
{ text: '前端 API', link: '/develop/fe-api.md', },
|
||||
{ text: '开发文档', link: '/develop/index.md', },
|
||||
],
|
||||
},
|
||||
@@ -109,11 +111,11 @@ export default defineConfig({
|
||||
text: "部署说明",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Docker', link: '/guide/backend/docker.md' },
|
||||
{ text: '守护进程', link: '/guide/backend/daemon.md' },
|
||||
{ text: '反向代理', link: '/guide/backend/reverse-proxy.md' },
|
||||
{ text: '编译构建', link: '/guide/backend/build.md' },
|
||||
{ text: '程序升级', link: '/guide/backend/update.md' },
|
||||
{ text: 'Docker', link: '/guide/backend/docker.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,53 +1,63 @@
|
||||
<template>
|
||||
<div ref="artalkEl" style="margin-top: 20px;"></div>
|
||||
<ClientOnly>
|
||||
<div ref="el" style="margin-top: 20px;"></div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, nextTick, ref, onMounted } from 'vue'
|
||||
import { watch, nextTick, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
import type Artalk from 'artalk'
|
||||
|
||||
const artalkEl = ref<HTMLElement | null>(null)
|
||||
const el = ref<HTMLElement | null>(null)
|
||||
|
||||
const router = useRouter()
|
||||
const page = useData().page
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
const script = document.createElement('script')
|
||||
script.src = `https://artalk.qwqaq.com/dist/Artalk.js`
|
||||
document.getElementsByTagName('head')[0].appendChild(script)
|
||||
script.onload = () => {
|
||||
initArtalk(page.value)
|
||||
}
|
||||
initArtalk(getConfByPage())
|
||||
})
|
||||
|
||||
watch(() => router.route.data.relativePath, (path) => {
|
||||
watch(() => router.route.data.relativePath, () => {
|
||||
nextTick(() => {
|
||||
Artalk.update(getArtalkConfByPage(page.value))
|
||||
Artalk.reload()
|
||||
artalk.update(getConfByPage())
|
||||
artalk.reload()
|
||||
})
|
||||
})
|
||||
|
||||
function getArtalkConfByPage(page: any) {
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
|
||||
function initArtalk(conf: any) {
|
||||
import('artalk').then(({ default: Artalk }) => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
gravatar: {
|
||||
mirror: 'https://cravatar.cn/avatar/'
|
||||
},
|
||||
...conf
|
||||
})
|
||||
}).then(() => {
|
||||
loadExtraFuncs()
|
||||
})
|
||||
}
|
||||
|
||||
function getConfByPage() {
|
||||
return {
|
||||
pageKey: 'https://artalk.js.org'+location.pathname,
|
||||
pageTitle: page.title,
|
||||
pageTitle: page.value.title,
|
||||
server: 'https://artalk.qwqaq.com',
|
||||
site: 'ArtalkDocs',
|
||||
}
|
||||
}
|
||||
|
||||
function initArtalk(page: any) {
|
||||
Artalk.init({
|
||||
el: artalkEl.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
gravatar: {
|
||||
mirror: 'https://cravatar.cn/avatar/'
|
||||
},
|
||||
...getArtalkConfByPage(page)
|
||||
})
|
||||
|
||||
function loadExtraFuncs() {
|
||||
// 图片灯箱插件
|
||||
Artalk.on('list-loaded', () => {
|
||||
artalk.on('list-loaded', () => {
|
||||
document.querySelectorAll('.atk-comment .atk-content').forEach(($content) => {
|
||||
const imgEls = $content.querySelectorAll<HTMLImageElement>('img:not([atk-emoticon]):not([atk-lightbox])');
|
||||
imgEls.forEach((imgEl) => {
|
||||
@@ -66,7 +76,7 @@ function initArtalk(page: any) {
|
||||
|
||||
// 夜间模式
|
||||
const darkMode = document.querySelector('html').classList.contains('dark')
|
||||
Artalk.setDarkMode(darkMode)
|
||||
artalk.setDarkMode(darkMode)
|
||||
|
||||
new MutationObserver((mList) => {
|
||||
mList.forEach((m) => {
|
||||
@@ -74,7 +84,7 @@ function initArtalk(page: any) {
|
||||
|
||||
// @ts-ignore
|
||||
const darkMode = m.target.classList.contains('dark')
|
||||
Artalk.setDarkMode(darkMode)
|
||||
artalk.setDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html'), { attributes: true })
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"latest": "2.6.4"
|
||||
"latest": "2.7.1"
|
||||
}
|
||||
+31
-17
@@ -4,23 +4,39 @@
|
||||
|
||||
|事件名|描述|
|
||||
|-|-|
|
||||
| `list-load` | 评论加载事件 |
|
||||
| `list-loaded` | 评论加载完成事件 |
|
||||
| `list-inserted` | 新增评论插入事件 |
|
||||
| `editor-submit` | 编辑器提交事件 |
|
||||
| `editor-submitted` | 编辑器提交完成事件 |
|
||||
| `user-changed` | 本地用户数据变更事件 |
|
||||
| `conf-loaded` | 配置变更事件 |
|
||||
| `sidebar-show` | 侧边栏显示事件 |
|
||||
| `sidebar-hide` | 侧边栏隐藏事件 |
|
||||
| `inited` | 初始化后 |
|
||||
| `destroy` | 实例销毁时 |
|
||||
| `list-fetch` | 评论列表请求时 |
|
||||
| `list-fetched` | 评论列表请求后 |
|
||||
| `list-load` | 评论装载前 |
|
||||
| `list-loaded` | 评论装载后 |
|
||||
| `list-error` | 评论加载错误时 |
|
||||
| `list-goto-first` | 评论列表归位时 |
|
||||
| `list-reach-bottom` | 评论列表滚动到底部时 |
|
||||
| `list-reach-bottom` | 评论列表滚动到底部时 |
|
||||
| `comment-inserted` | 评论插入后 |
|
||||
| `comment-updated` | 评论更新后 |
|
||||
| `comment-deleted` | 评论删除后 |
|
||||
| `comment-rendered` | 评论节点渲染后 |
|
||||
| `unreads-updated` | 未读消息变更时 |
|
||||
| `list-goto` | 评论跳转时 |
|
||||
| `page-loaded` | 页面数据更新后 |
|
||||
| `editor-submit` | 编辑器提交时 |
|
||||
| `editor-submitted` | 编辑器提交后 |
|
||||
| `user-changed` | 本地用户数据变更时 |
|
||||
| `conf-loaded` | 配置变更时 |
|
||||
| `dark-mode-changed` | 深色模式变更时 |
|
||||
| `sidebar-show` | 侧边栏显示 |
|
||||
| `sidebar-hide` | 侧边栏隐藏 |
|
||||
|
||||
|
||||
事件声明代码:[@ArtalkJS/Artalk - types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.ts)
|
||||
|
||||
## 添加事件监听
|
||||
|
||||
```js
|
||||
Artalk.use(ctx => {
|
||||
ctx.on('list-loaded', () => {
|
||||
alert('评论已加载完毕')
|
||||
})
|
||||
artalk.on('list-loaded', () => {
|
||||
alert('评论已加载完毕')
|
||||
})
|
||||
```
|
||||
|
||||
@@ -29,8 +45,6 @@ Artalk.use(ctx => {
|
||||
```js
|
||||
let foo = function() { /* do something */ }
|
||||
|
||||
Artalk.use(ctx => {
|
||||
ctx.on('list-loaded', foo)
|
||||
ctx.off('list-loaded', foo)
|
||||
})
|
||||
artalk.on('list-loaded', foo)
|
||||
artalk.off('list-loaded', foo)
|
||||
```
|
||||
+93
-40
@@ -1,11 +1,23 @@
|
||||
# Frontend API
|
||||
|
||||
## 初始化 `Artalk.init`
|
||||
## 安装依赖
|
||||
|
||||
创建 Artalk 实例化对象。
|
||||
用你喜欢的包管理器安装 Artalk:
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
# 或者使用 npm
|
||||
npm install artalk
|
||||
```
|
||||
|
||||
## 创建实例 `init`
|
||||
|
||||
调用该函数将创建并得到一个 Artalk 的实例化对象,可用于后续操作。
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
import Artalk from 'artalk'
|
||||
|
||||
const artalk = Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '/post/1',
|
||||
pageTitle: '关于引入 Artalk 的这档子事',
|
||||
@@ -14,93 +26,134 @@ Artalk.init({
|
||||
})
|
||||
```
|
||||
|
||||
## 更新配置 `Artalk.update`
|
||||
调用该函数会向后端发起请求:
|
||||
|
||||
修改 Artalk 当前配置。
|
||||
1. 获取前端配置
|
||||
2. 获取评论列表
|
||||
|
||||
```js
|
||||
Artalk.update({
|
||||
// 新的配置...
|
||||
})
|
||||
```
|
||||
配置和评论加载完毕后会分别触发 `conf-loaded` 和 `list-loaded` 事件。
|
||||
|
||||
该函数调用后,`conf-loaded` 事件将被触发。
|
||||
注:前端配置可能会被覆盖,详情见:[在后端控制前端](../guide/backend/fe-control.md)。
|
||||
|
||||
更新配置不会自动刷新评论列表,可按需继续执行 `Artalk.reload()` 函数。
|
||||
|
||||
注:前端的一些配置将会被后端的配置覆盖,详情见:[在后端控制前端](../guide/backend/fe-control.md)
|
||||
|
||||
## 重新加载 `Artalk.reload`
|
||||
|
||||
刷新 Artalk 评论列表。
|
||||
|
||||
```js
|
||||
Artalk.reload()
|
||||
```
|
||||
|
||||
列表加载前将触发 `list-load` 事件,加载完毕后将触发 `list-loaded` 事件。
|
||||
|
||||
## 释放资源 `Artalk.destroy`
|
||||
## 释放资源 `destroy`
|
||||
|
||||
销毁 Artalk 实例,用于资源释放。
|
||||
|
||||
```js
|
||||
Artalk.destroy()
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.destroy()
|
||||
```
|
||||
|
||||
## 事件监听 `Artalk.on`
|
||||
释放资源将执行 Artalk 挂载的 DOM 节点删除,以及解除 Artalk 事件监听等操作。
|
||||
|
||||
在 Vue / React 等框架中,务必在组件销毁时调用该函数,否则会造成内存泄漏:
|
||||
|
||||
```ts
|
||||
import { onUnmounted } from 'vue'
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
```
|
||||
|
||||
## 更新配置 `update`
|
||||
|
||||
修改 Artalk 当前配置。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.update({
|
||||
// 新的配置...
|
||||
})
|
||||
```
|
||||
|
||||
调用该函数将触发 `conf-loaded` 事件。
|
||||
|
||||
更新配置不会自动刷新评论列表,可按需继续执行 `artalk.reload()` 函数。
|
||||
|
||||
请注意,该函数是一个实例化对象中的方法,并非全局函数。
|
||||
|
||||
前端配置参考:[前端配置](../guide/frontend/config.md)
|
||||
|
||||
## 重新加载 `reload`
|
||||
|
||||
重新从后端加载 Artalk 评论列表数据。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.reload()
|
||||
```
|
||||
|
||||
列表加载前将触发 `list-load` 事件,加载完毕后将触发 `list-loaded` 事件。
|
||||
|
||||
## 监听事件 `on`
|
||||
|
||||
添加 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
Artalk.on('list-loaded', () => {
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.on('list-loaded', () => {
|
||||
alert('评论列表加载完毕')
|
||||
})
|
||||
```
|
||||
|
||||
可监听事件类型见:[前端界面事件](./event.md)
|
||||
可参考:[前端 Event](./event.md)。
|
||||
|
||||
## 解除监听 `Artalk.off`
|
||||
## 解除监听 `off`
|
||||
|
||||
解除 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
const handler = () => {
|
||||
alert('评论列表加载完毕')
|
||||
}
|
||||
|
||||
Artalk.on('list-loaded', handler)
|
||||
Artalk.off('list-loaded', handler)
|
||||
artalk.on('list-loaded', handler)
|
||||
artalk.off('list-loaded', handler)
|
||||
```
|
||||
|
||||
## 触发事件 `Artalk.trigger`
|
||||
## 触发事件 `trigger`
|
||||
|
||||
触发 Artalk 事件。
|
||||
|
||||
```js
|
||||
Artalk.trigger('list-loaded')
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.trigger('list-loaded')
|
||||
```
|
||||
|
||||
## 扩展插件 `Artalk.use`
|
||||
## 加载插件 `use`
|
||||
|
||||
该 API 用于扩展 Artalk。
|
||||
通过 `Artalk.use` 函数加载插件。请注意,该函数是一个全局函数,并非实例化对象中的方法。通过该函数加载的插件将对所有 Artalk 实例生效。
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
详情见:[插件开发](./plugs.md)
|
||||
|
||||
## 夜间模式 `Artalk.setDarkMode`
|
||||
## 夜间模式 `setDarkMode`
|
||||
|
||||
修改夜间模式,可以配合博客主题调用,例如当用户点击夜间模式切换按钮。
|
||||
|
||||
```js
|
||||
Artalk.setDarkMode(true)
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
## 浏览量组件 `Artalk.loadCountWidget`
|
||||
## 浏览量组件 `loadCountWidget`
|
||||
|
||||
详情见:[浏览量统计](../guide/frontend/pv.md)
|
||||
@@ -23,6 +23,6 @@
|
||||
* [@ArtalkJS/Artalk - server/server.go](https://github.com/ArtalkJS/Artalk/blob/master/server/server.go)
|
||||
- `UI` 及 `Event` 部分参考源码
|
||||
* [@ArtalkJS/Artalk - src/artalk.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/artalk.ts)
|
||||
* [@ArtalkJS/Artalk - types/event.d.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.d.ts)
|
||||
* [@ArtalkJS/Artalk - types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.ts)
|
||||
|
||||
通过 Artalk 提供的 `API` 和 `Event`,你可以实现很多高级功能,比如编写评论管理机器人、评论提醒推送插件等。Artalk 并不为此提供技术指导,但鼓励你参考此处的文档自行定制。
|
||||
+50
-3
@@ -1,17 +1,64 @@
|
||||
# 插件开发
|
||||
|
||||
你可以使用 `Artalk.use` 来扩展 Artalk。
|
||||
使用 `Artalk.use` 来扩展 Artalk。
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
ctx.on('inited', () => {
|
||||
ctx.get('editor').setContent("Hello World")
|
||||
})
|
||||
|
||||
ctx.on('list-loaded', () => {
|
||||
console.log('评论列表加载完毕')
|
||||
})
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
请注意:
|
||||
|
||||
- `Artalk.use` 必须在 `Artalk.init` 之前调用。
|
||||
- 请勿依赖插件的加载顺序,请监听事件来执行插件逻辑。
|
||||
- 当所有的插件加载完毕后,会触发 `inited` 事件。
|
||||
|
||||
在 use 函数中,你可以通过 `ctx` 来访问 Artalk 的上下文。
|
||||
|
||||
## Context
|
||||
|
||||
Context 对象包含了 Artalk 的上下文信息。
|
||||
|
||||
| 成员 | 说明 |
|
||||
| --- | --- |
|
||||
| `ctx.$root` | Artalk 容器元素 |
|
||||
| `ctx.conf` | Artalk 配置 |
|
||||
| `ctx.fetch` | 加载数据数据 |
|
||||
| `ctx.reload` | 重载评论列表 |
|
||||
| `ctx.on` | 添加事件监听 |
|
||||
| `ctx.off` | 解除事件监听 |
|
||||
| `ctx.trigger` | 触发事件 |
|
||||
| `ctx.inject` | 依赖注入 |
|
||||
| `ctx.get` | 获取依赖 |
|
||||
|
||||
::: warning
|
||||
Context API 目前仍不稳定,开发可能会有变动,升级请关注 CHANGELOG。
|
||||
:::
|
||||
|
||||
参考:[@artalk/types/context.d.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/context.d.ts)
|
||||
参考:[@artalk/types/context.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/context.ts)
|
||||
|
||||
Artalk 拥有完整的 TypeScript 类型定义,你可以通过编辑器的自动补全来查看 API。
|
||||
|
||||
## 示例插件
|
||||
|
||||
Artalk 有很多内置的插件,你可以参考它们的源码来开发自己的插件。
|
||||
|
||||
[@ArtalkJS/Artalk - src/plugins](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/artalk/src/plugins)
|
||||
|
||||
我们还提供了一些外置的插件,同样可以参考:
|
||||
|
||||
| 插件 | 说明 |
|
||||
| --- | --- |
|
||||
| [@artalk/plugin-katex](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/plugin-katex) | LaTeX 公式插件 |
|
||||
| [@artalk/plugin-lightbox](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/plugin-lightbox) | 图片灯箱插件 |
|
||||
+2
-3
@@ -2,11 +2,11 @@
|
||||
layout: home
|
||||
|
||||
title: Artalk
|
||||
titleTemplate: The Selfhosted Comment System
|
||||
titleTemplate: A Self-hosted Comment System
|
||||
|
||||
hero:
|
||||
name: Artalk
|
||||
text: The Selfhosted Comment System
|
||||
text: A Self-hosted Comment System
|
||||
tagline: Elevate your online conversations with a comment system that embodies the trifecta of lightness, security, and refinement.
|
||||
actions:
|
||||
- theme: brand
|
||||
@@ -30,4 +30,3 @@ features:
|
||||
title: Flexible
|
||||
details: Use Golang to interpret the excellence of multiple platforms
|
||||
---
|
||||
|
||||
@@ -113,7 +113,7 @@ admin_notify:
|
||||
|
||||

|
||||
|
||||
详情可参考:[“Bots: An introduction for developers - Telegram”](https://core.telegram.org/bots)
|
||||
详情可参考:[Bots: An introduction for developers - Telegram](https://core.telegram.org/bots)
|
||||
|
||||
::: tip
|
||||
|
||||
@@ -151,7 +151,7 @@ admin_notify:
|
||||
|
||||
<img src="/images/notify/lark-3.png" width="400px">
|
||||
|
||||
可参考:[“飞书帮助中心文档”](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
|
||||
可参考:[飞书帮助中心文档](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
|
||||
|
||||
## 钉钉
|
||||
|
||||
@@ -164,7 +164,7 @@ admin_notify:
|
||||
secret: ""
|
||||
```
|
||||
|
||||
可参考:[“钉钉开放文档”](https://open.dingtalk.com/document/robots/custom-robot-access)
|
||||
可参考:[钉钉开放文档](https://open.dingtalk.com/document/robots/custom-robot-access)
|
||||
|
||||
## Bark
|
||||
|
||||
@@ -227,7 +227,7 @@ admin_notify:
|
||||
{{link_to_reply}}
|
||||
```
|
||||
|
||||
可用变量和邮件模板相同,可参考:[“邮件模版”](./email.md#邮件模板)
|
||||
可用变量和邮件模板相同,可参考:[邮件模版](./email.md#邮件模板)
|
||||
|
||||
## 嘈杂模式 `noise_mode`
|
||||
|
||||
@@ -264,7 +264,7 @@ admin_notify:
|
||||
|`notify_subject`|通知标题 |String| 对应 admin_notify.notify_subject 配置项 |
|
||||
|`notify_body` |通知内容 |String| 根据 admin_notify.notify_tpl 模版渲染 |
|
||||
|`comment` |评论内容 |Object| 新创建的评论数据对象 |
|
||||
|`parent_comment`|评论回复的目标|Object| Root 根节点评论类型改项为 null |
|
||||
|`parent_comment`|评论回复的目标|Object| 如果是根节点评论值为 null |
|
||||
|
||||
**Body 数据样本**
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ app_key: "<任意的字符>"
|
||||
locale: "zh-CN"
|
||||
```
|
||||
|
||||
详情参考:[“多语言”](../frontend/i18n.md)
|
||||
详情参考:[多语言](../frontend/i18n.md)
|
||||
|
||||
## 数据库 `db`
|
||||
|
||||
@@ -118,7 +118,7 @@ db:
|
||||
|
||||
Artalk 支持多站点,你可以创建多个管理员账户,为其分配站点,让你的朋友们共用同一个后端程序。
|
||||
|
||||
详情参考:[“管理员 × 多站点”](/guide/backend/multi-site.md)
|
||||
详情参考:[管理员 × 多站点](/guide/backend/multi-site.md)
|
||||
|
||||
## 可信域名 `trusted_domains`
|
||||
|
||||
@@ -178,31 +178,31 @@ Artalk.init({ site: "Artalk 官网" })
|
||||
|
||||
## 前端配置 `frontend`
|
||||
|
||||
增加 `frontend` 字段内容可以在后端控制前端的配置,详情可参考:[“在后端控制前端”](/guide/backend/fe-control)。
|
||||
增加 `frontend` 字段内容可以在后端控制前端的配置,详情可参考:[在后端控制前端](/guide/backend/fe-control)。
|
||||
|
||||
## 邮件通知 `email`
|
||||
|
||||
配置邮件通知,让回复通过邮件的形式通知目标用户,你可以自定义邮件发送者名称、标题、模版等。
|
||||
|
||||
详情参考:[“后端 · 邮件通知”](/guide/backend/email.md)
|
||||
详情参考:[后端 · 邮件通知](/guide/backend/email.md)
|
||||
|
||||
## 多元推送 `admin_notify`
|
||||
|
||||
你可以配置多种消息发送方式,例如飞书、Telegram 等,当收到新的评论时通知管理员。
|
||||
|
||||
详情参考:[“后端 · 多元推送”](/guide/backend/admin_notify.md)
|
||||
详情参考:[后端 · 多元推送](/guide/backend/admin_notify.md)
|
||||
|
||||
## 评论审核 `moderator`
|
||||
|
||||
配置评论审核来自动拦截垃圾评论。
|
||||
|
||||
详情参考:[“后端 · 评论审核”](/guide/backend/moderator.md)
|
||||
详情参考:[后端 · 评论审核](/guide/backend/moderator.md)
|
||||
|
||||
## 验证码 `captcha`
|
||||
|
||||
支持图片、滑动验证码,通过验证码对请求频率进行限制。
|
||||
|
||||
详情参考:[“后端 · 验证码”](/guide/backend/captcha.md)
|
||||
详情参考:[后端 · 验证码](/guide/backend/captcha.md)
|
||||
|
||||
## 高速缓存 `cache`
|
||||
|
||||
@@ -348,5 +348,5 @@ test_file: "./data/artalk.log"
|
||||
|
||||
配置文件相关代码:[/internal/config/config.go](https://github.com/ArtalkJS/Artalk/blob/master/internal/config/config.go)
|
||||
|
||||
前往:[“前端配置”](/guide/frontend/config.md)
|
||||
前往:[前端配置](/guide/frontend/config.md)
|
||||
:::
|
||||
+124
-55
@@ -1,5 +1,129 @@
|
||||
# 守护进程
|
||||
|
||||
## Systemd
|
||||
|
||||
前提条件:
|
||||
|
||||
- Artalk 二进制文件,可在 [GitHub Release](https://github.com/ArtalkJS/Artalk/releases) 下载
|
||||
- `systemctl --version` 232 或更新的版本
|
||||
- `sudo` 管理员权限
|
||||
|
||||
移动 Artalk 到 `$PATH`,例如:
|
||||
|
||||
```bash
|
||||
sudo mv artalk /usr/bin/
|
||||
```
|
||||
|
||||
测试是否有效:
|
||||
|
||||
```bash
|
||||
artalk version
|
||||
```
|
||||
|
||||
创建名为 artalk 的用户组:
|
||||
|
||||
```bash
|
||||
sudo groupadd --system artalk
|
||||
```
|
||||
|
||||
创建一个名为 artalk 的用户,并且拥有一个可写的 home 目录:
|
||||
|
||||
```bash
|
||||
sudo useradd --system \
|
||||
--gid artalk \
|
||||
--create-home \
|
||||
--home-dir /var/lib/artalk \
|
||||
--shell /usr/sbin/nologin \
|
||||
--comment "Artalk server" \
|
||||
artalk
|
||||
```
|
||||
|
||||
如果你有 Artalk 的配置文件,请确保刚刚创建的 artalk 用户有权限可以读取。
|
||||
|
||||
创建服务文件:
|
||||
|
||||
```bash
|
||||
sudo vim /etc/systemd/system/artalk.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Artalk
|
||||
Documentation=https://artalk.js.org
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=artalk
|
||||
Group=artalk
|
||||
ExecStart=/usr/bin/artalk server -w /var/lib/artalk -c /etc/artalk/artalk.yml
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
ExecStop=/bin/kill -s QUIT $MAINPID
|
||||
TimeoutStopSec=5s
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=512
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
请仔细检查 `ExecStart` 和 `ExecReload`。 确保二进制文件的位置和程序启动参数是正确的
|
||||
|
||||
例如:更改 `-c` 参数的路径来指定配置文件,`-w` 参数可以更改工作目录。
|
||||
|
||||
请注意配置文件中的所有相对路径都是基于工作目录,例如配置文件中的 `./data/` 文件夹,如果启动参数 `-w /var/lib/artalk`,将读取 `/var/lib/artalk/data/` 目录。请确保该目录中的文件对于创建的 `artalk` 账户有权限可读写。
|
||||
|
||||
保存服务文件后,你可以设置自动启动服务:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now artalk
|
||||
```
|
||||
|
||||
验证服务是否正常运行:
|
||||
|
||||
```bash
|
||||
systemctl status artalk
|
||||
```
|
||||
|
||||
一些常用的命令:
|
||||
|
||||
- 启动服务:`systemctl start artalk`
|
||||
- 停止服务:`systemctl stop artalk`
|
||||
- 查看状态:`systemctl status artalk`
|
||||
- 查看日志:`journalctl -u artalk --no-pager | less +G`
|
||||
|
||||
## Tmux
|
||||
|
||||
tmux 将创建一个持续的命令行会话,在 SSH 或 tty 断开后保持在后台。
|
||||
|
||||
Note: 服务器关闭或重启后,tmux 会话将被清除,需要手动重新运行程序。
|
||||
|
||||
1. 创建会话 `tmux new -s artalk`
|
||||
2. 运行程序 `./artalk server`
|
||||
|
||||
恢复接入会话:`tmux attach -t artalk`
|
||||
|
||||
查看所有会话:`tmux ls`
|
||||
|
||||
## Supervisor
|
||||
|
||||
以宝塔面板举例:打开「软件商店」,搜索并安装「Supervisor管理器」:
|
||||
|
||||

|
||||
|
||||
安装后,打开插件,点击「添加守护程序」:
|
||||
|
||||

|
||||
|
||||
> - 启动用户:`root` 或其他
|
||||
> - 运行目录:点击右侧图标,选择 Artalk 所在目录
|
||||
> - 启动命令:`./artalk server`
|
||||
|
||||
## Docker
|
||||
|
||||
更新 Docker 容器的 [Restart 策略](https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) 以达到进程守护效果。
|
||||
@@ -18,58 +142,3 @@ services:
|
||||
artalk:
|
||||
+ restart: unless-stopped
|
||||
```
|
||||
|
||||
## tmux
|
||||
|
||||
tmux 将创建一个持续的命令行会话,在 SSH 或 tty 断开后保持在后台。
|
||||
|
||||
Note: 服务器关闭或重启后,tmux 会话将被清除,需要手动重新运行程序。
|
||||
|
||||
1. 创建会话 `tmux new -s artalk`
|
||||
2. 运行程序 `./artalk server`
|
||||
|
||||
恢复接入会话:`tmux attach -t artalk`
|
||||
|
||||
查看所有会话:`tmux ls`
|
||||
|
||||
## systemd
|
||||
|
||||
`sudo vim /etc/systemd/system/artalk.service`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Artalk
|
||||
After=network.target remote-fs.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
ExecStart=<Artalk 执行文件绝对路径> server -w <工作目录绝对路径> -c <配置文件相对于工作目录路径>
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
ExecStop=/bin/kill -s QUIT $MAINPID
|
||||
Restart=on-abnormal
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
- 更新 systemd 配置:`systemctl daemon-reload`
|
||||
- 启动:`systemctl start artalk.service`
|
||||
- 停止:`systemctl stop artalk.service`
|
||||
- 状态:`systemctl status artalk.service`
|
||||
|
||||
Tip: 设置 `alias` 简化命令输入;Artalk 参数 `-w` 用于指定工作目录,配置文件中的所有「相对路径」会基于该目录,例如 `./data/` 文件夹。
|
||||
|
||||
## Supervisor
|
||||
|
||||
以宝塔面板举例:打开「软件商店」,搜索并安装「Supervisor管理器」:
|
||||
|
||||

|
||||
|
||||
安装后,打开插件,点击「添加守护程序」:
|
||||
|
||||

|
||||
|
||||
> - 启动用户:`root` 或其他
|
||||
> - 运行目录:点击右侧图标,选择 Artalk 所在目录
|
||||
> - 启动命令:`./artalk server`
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
## 备份
|
||||
|
||||
前往:[“数据迁移 · 备份”](/guide/transfer.md)
|
||||
前往:[数据迁移 · 备份](/guide/transfer.md)
|
||||
@@ -12,11 +12,11 @@ Artalk 提供后端程序的 Docker 镜像,以便加速部署流程,提供
|
||||
|
||||
:::tip
|
||||
|
||||
推荐使用 Docker Compose:[“后端部署”](/guide/backend/install) 页面已详细讲解。
|
||||
推荐使用 Docker Compose,可参考 [程序部署](/guide/deploy) 页面的步骤。
|
||||
|
||||
:::
|
||||
|
||||
常规的 Docker 容器创建可参考:
|
||||
Docker 容器创建:
|
||||
|
||||
```bash
|
||||
# 为 Artalk 创建一个目录
|
||||
@@ -112,4 +112,4 @@ docker exec -it artalk bash
|
||||
|
||||
## 多平台兼容性
|
||||
|
||||
Docker 镜像暂仅提供 amd_64 构建,若需要在 ARM 架构运行,请下载 [二进制编译构建](/guide/backend/install.md#普通方式)
|
||||
Docker 镜像暂仅提供 x86、arm64 的镜像构建,如需更多平台架构版本,请下载 [二进制构建部署](/guide/deploy.md#普通方式部署)。
|
||||
@@ -324,4 +324,4 @@ admin_notify:
|
||||
|
||||
注:旧版 `email.mail_subject_to_admin` 配置项已弃用,请使用以上替代。
|
||||
|
||||
不局限于邮件,Artalk 支持多种方式向管理员发送通知,参考:[“多元推送”](./admin_notify.md#邮件通知)。
|
||||
不局限于邮件,Artalk 支持多种方式向管理员发送通知,参考:[多元推送](./admin_notify.md#邮件通知)。
|
||||
@@ -60,7 +60,7 @@ Artalk.init({
|
||||
|
||||
建议 `frontend.emoticons` 填入表情包的 URL,而非 Object。
|
||||
|
||||
参考文档:[“表情包”](/guide/frontend/emoticons)
|
||||
参考文档:[表情包](/guide/frontend/emoticons)
|
||||
|
||||
如需传递 Object,可以将其转为 JSON 字符串格式,例如:
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ img_upload:
|
||||
|
||||
频率限制跟随 `captcha` 验证码配置,当超出限制将弹出验证码。
|
||||
|
||||
可参考:[“后端 · 验证码”](/guide/backend/captcha.md)
|
||||
可参考:[后端 · 验证码](/guide/backend/captcha.md)
|
||||
|
||||
## path
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ moderator:
|
||||
|
||||
## 腾讯云文本内容安全
|
||||
|
||||
可参考:[“腾讯云文档”](https://cloud.tencent.com/document/product/1124/64508)
|
||||
可参考:[腾讯云文档](https://cloud.tencent.com/document/product/1124/64508)
|
||||
|
||||
开通「文本内容安全」后,在「访问管理」-「API 密钥管理」新增具有权限的 Secret,然后填入配置:
|
||||
|
||||
@@ -78,7 +78,7 @@ moderator:
|
||||
|
||||
## 阿里云内容安全
|
||||
|
||||
可参考:[“阿里云文档”](https://help.aliyun.com/document_detail/28417.html)
|
||||
可参考:[阿里云文档](https://help.aliyun.com/document_detail/28417.html)
|
||||
|
||||
开通「阿里云内容安全」后,阿里云后台创建 Access Key 并填入配置:
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ docker exec -it artalk artalk admin
|
||||
|
||||
## 站点的创建和管理
|
||||
|
||||
你可以在侧边栏「[控制中心](../frontend/sidebar.md#控制中心)」创建多个站点,管理站点和快速切换站点,详情可参考:[“侧边栏”](/guide/frontend/sidebar.html)。
|
||||
你可以在侧边栏「[控制中心](../frontend/sidebar.md#控制中心)」创建多个站点,管理站点和快速切换站点,详情可参考:[侧边栏](/guide/frontend/sidebar.html)。
|
||||
|
||||
## 管理员配置
|
||||
|
||||
@@ -66,7 +66,7 @@ admin_users:
|
||||
|
||||
然后配置填写:`"(bcrypt)$2y$10$ti4vZYIrxVN8rLcY..."`,以 `(bcrypt)` 开头。
|
||||
|
||||
命令解释参考:[“Compute bcrypt hash from command line”](https://unix.stackexchange.com/questions/307994/compute-bcrypt-hash-from-command-line#answer-419855)
|
||||
命令解释参考:[Compute bcrypt hash from command line](https://unix.stackexchange.com/questions/307994/compute-bcrypt-hash-from-command-line#answer-419855)
|
||||
|
||||
- **badge_name**:用户显示的头衔徽标文字。
|
||||
- **badge_color**:用户显示的头衔徽标背景颜色。
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# 反向代理
|
||||
|
||||
## Caddy
|
||||
|
||||
**/etc/caddy/Caddyfile**
|
||||
|
||||
```nginx
|
||||
artalk.your_domain.com {
|
||||
tls /etc/caddy/ssl/cert.pem /etc/caddy/ssl/cert.key
|
||||
|
||||
reverse_proxy http://localhost:23366 {
|
||||
header_up X-Forwarded-For {header.X-Forwarded-For}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
执行重载命令:
|
||||
|
||||
```sh
|
||||
sudo systemctl reload caddy
|
||||
```
|
||||
|
||||
## Nginx
|
||||
|
||||
假定:
|
||||
@@ -67,16 +87,6 @@ Artalk.init({ server: "http://artalk.your_domain.com" })
|
||||
尤其是运行在 Docker 容器内的 artalk,注意检查 IP 和端口是否能够被 Nginx 正常访问
|
||||
:::
|
||||
|
||||
## Caddy
|
||||
|
||||
创建 Caddyfile:
|
||||
|
||||
```
|
||||
artalk.your_domain.com {
|
||||
reverse_proxy / http://localhost:23366
|
||||
}
|
||||
```
|
||||
|
||||
## Apache
|
||||
|
||||
需要启用反代模块 `mod_proxy.c`
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
:::tip
|
||||
|
||||
执行 `./artalk upgrade -f` 携带参数 `-f` 来进行同版本号的补充更新。
|
||||
执行 `./artalk upgrade -f` 携带参数 `-f` 执行强制更新。
|
||||
|
||||
:::
|
||||
|
||||
## Docker 升级
|
||||
|
||||
可参考:[“Docker · 升级”](./docker.md#升级)
|
||||
可参考:[Docker · 升级](./docker.md#升级)
|
||||
|
||||
## 普通方式
|
||||
|
||||
|
||||
+42
-46
@@ -51,14 +51,11 @@ Artalk.init({
|
||||
|
||||
在控制台,你可以根据喜好配置评论系统、[将评论迁移到 Artalk](./transfer.md)。
|
||||
|
||||
祝贺!你已成功完成 Artalk 部署 🥳
|
||||
🥳 你已成功完成 Artalk 部署!
|
||||
|
||||
## 普通方式部署
|
||||
|
||||
1. 前往 [GitHub Release](https://github.com/ArtalkJS/Artalk/releases) 下载程序压缩包
|
||||
|
||||
> 注:我们已将 ArtalkGo 与主仓库合并,历史旧版可在[此页面](https://github.com/ArtalkJS/ArtalkGo/releases)查看,日后新版将在[新页面](https://github.com/ArtalkJS/Artalk/releases)发布。
|
||||
|
||||
2. 提取压缩包:`tar -zxvf artalk_版本号_系统_架构.tar.gz`
|
||||
3. 运行程序 `./artalk server`
|
||||
4. 前端配置
|
||||
@@ -67,25 +64,16 @@ Artalk.init({
|
||||
Artalk.init({ server: "http://your_domain:23366" })
|
||||
```
|
||||
|
||||
**其它可选操作:**
|
||||
**进一步操作:**
|
||||
|
||||
- [“反向代理端口到 80 / 443 (Nginx, Apache)”](./backend/reverse-proxy.md)
|
||||
- ["持久化运作 (tmux, systemd, supervisor)"](./backend/daemon.md)
|
||||
- [守护进程 (Systemd, Supervisor)](./backend/daemon.md)
|
||||
- [反向代理 (Caddy, Nginx, Apache)](./backend/reverse-proxy.md)
|
||||
|
||||
**附表:文件名释义表**
|
||||
如果有需要,你还可以自行编译程序,参考:[后端构建](./backend/build.md)。
|
||||
|
||||
|文件名|操作系统|CPU 架构|
|
||||
|:-|:-:|:-:|
|
||||
|artalk_linux_amd64.tar.gz|Linux|x86_64|
|
||||
|artalk_linux_arm64.tar.gz|Linux|ARM64|
|
||||
|artalk_linux_arm7.tar.gz|Linux|ARMv7|
|
||||
|artalk_windows_amd64.zip|Windows|x86_64|
|
||||
|artalk_darwin_arm64.tar.gz|macOS|Apple Silicon|
|
||||
|artalk_darwin_amd64.tar.gz|macOS|Intel Chip|
|
||||
## Compose 部署
|
||||
|
||||
## Docker Compose 部署
|
||||
|
||||
提供 docker-compose.yaml 文件可供参考:
|
||||
**compose.yaml**
|
||||
|
||||
```yaml
|
||||
version: "3.5"
|
||||
@@ -100,7 +88,7 @@ services:
|
||||
- ./data:/data
|
||||
```
|
||||
|
||||
在与配置文件相同的目录执行命令创建容器:
|
||||
执行命令创建容器:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
@@ -118,11 +106,7 @@ docker-compose exec artalk bash # 进入容器
|
||||
|
||||
:::
|
||||
|
||||
> 详细可见:[“后端 · Docker”](./backend/docker.md)
|
||||
|
||||
## 自行编译并运行
|
||||
|
||||
可参考:[“后端构建”](./backend/build.md)
|
||||
可参考:[Docker 部署文档](./backend/docker.md)
|
||||
|
||||
## CDN 资源
|
||||
|
||||
@@ -135,7 +119,21 @@ docker-compose exec artalk bash # 进入容器
|
||||
|
||||
Artalk 后端程序内嵌了前端 JS、CSS 文件,使用公共 CDN 资源请注意前后端版本的兼容性。
|
||||
|
||||
Artalk 静态资源通过上游 [CDNJS](https://cdnjs.com/) 分发,国内有许多镜像可供选择:
|
||||
Artalk 静态资源通过上游 CDNJS 分发,以下镜像资源可供选择:
|
||||
|
||||
**CDNJS**
|
||||
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**Staticfile CDN (国内)**
|
||||
|
||||
> https://cdn.staticfile.org/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdn.staticfile.org/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
::: details 查看更多
|
||||
|
||||
**BootCDN (国内)**
|
||||
|
||||
@@ -143,18 +141,11 @@ Artalk 静态资源通过上游 [CDNJS](https://cdnjs.com/) 分发,国内有
|
||||
>
|
||||
> https://cdn.bootcdn.net/ajax/libs/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**75CDN (国内)**
|
||||
|
||||
**ElemeCDN (国内)**
|
||||
|
||||
> https://npm.elemecdn.com/artalk@:ArtalkVersion:/dist/Artalk.js
|
||||
> https://lib.baomitu.com/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://npm.elemecdn.com/artalk@:ArtalkVersion:/dist/Artalk.css
|
||||
|
||||
**CDNJS**
|
||||
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.css
|
||||
> https://lib.baomitu.com/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**UNPKG**
|
||||
|
||||
@@ -168,11 +159,9 @@ Artalk 静态资源通过上游 [CDNJS](https://cdnjs.com/) 分发,国内有
|
||||
>
|
||||
> https://cdn.jsdelivr.net/npm/artalk@:ArtalkVersion:/dist/Artalk.css
|
||||
|
||||
## ArtalkLite
|
||||
:::
|
||||
|
||||
可选择精简版 [ArtalkLite](./frontend/artalk-lite.md):体积更小、更简约。
|
||||
|
||||
## Node 环境
|
||||
## Node 项目
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
@@ -189,13 +178,20 @@ Artalk.init({
|
||||
})
|
||||
```
|
||||
|
||||
## 何时引入、何时 init?
|
||||
可参考:
|
||||
|
||||
- 可以在任意位置引入 JS 和 CSS 资源,但需确保 JS 引入在执行 `Artalk.init({})` 前。
|
||||
- 执行 `Artalk.init({ el: '#x' })` 时,需要确保 `<div id="x"></div>` 存在于页面当中。
|
||||
|
||||
可参考:[“前端框架引入”](./frontend/import-framework.md) / [“博客引入”](./frontend/import-blog.md)
|
||||
- [置入框架文档](./frontend/import-framework.md)
|
||||
- [置入博客文档](./frontend/import-blog.md)
|
||||
- [前端 API](../develop/fe-api.md)
|
||||
|
||||
## 数据导入
|
||||
|
||||
从其他评论系统导入数据:[“数据迁移”](./transfer.md)
|
||||
从其他评论系统导入数据:[数据迁移](./transfer.md)
|
||||
|
||||
## ArtalkLite
|
||||
|
||||
可选择精简版 [ArtalkLite](./frontend/artalk-lite.md):体积更小、更简约。
|
||||
|
||||
## 开发环境
|
||||
|
||||
可参考:[开发者指南](https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md)
|
||||
@@ -14,8 +14,9 @@ Artalk 离不开社区的支持与帮助,**是你们让 Artalk 走得更远!
|
||||
- [Vuepress配置评论插件为Artalk](https://wiki.eryajf.net/pages/b74c2b/)
|
||||
- [Fly.io 部署 Artalk 评论系统](https://wayjam.me/posts/artalk-deploy-on-flyio/)
|
||||
- [Railway.app 部署 Artalk 评论系统](https://blog.moraxyc.com/post/eidb8276/)
|
||||
- [Render.com 部署Artalk自托管评论系统](https://www.leenhawk.com/posts/3932123804/)
|
||||
|
||||
更多新版读物正在撰写当中(
|
||||
更多新版读物正在撰写当中...
|
||||
|
||||
## Artalk v1
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
# 前端配置
|
||||
|
||||
```js
|
||||
Artalk.init({ 你的配置... })
|
||||
Artalk.update({ 更新配置... })
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.update({ ... })
|
||||
```
|
||||
|
||||
- 默认配置:[defaults.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/defaults.ts)
|
||||
- 声明文件:[artalk-config.d.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/artalk-config.d.ts)
|
||||
- 声明文件:[config.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/config.ts)
|
||||
|
||||
## 轻松配置
|
||||
|
||||
推荐在侧边栏 “[控制中心](/guide/frontend/sidebar.md)” 通过图形界面修改前端的配置,而无需在代码中对界面进行设定。
|
||||
|
||||
注:前端的设定可能会被后端覆盖,更多内容参考:[“在后端控制前端”](/guide/backend/fe-control.md)
|
||||
注:前端的设定可能会被后端覆盖,更多内容参考:[在后端控制前端](/guide/backend/fe-control.md)
|
||||
|
||||
## 基本配置(必填项)
|
||||
|
||||
@@ -36,7 +37,7 @@ Artalk.update({ 更新配置... })
|
||||
|
||||
可以填写由博客系统生成的 `固定链接`,但建议使用相对路径以便日后切换域名。
|
||||
|
||||
参考:[“关于使用相对 / 绝对路径”](/guide/backend/relative-path.md)
|
||||
参考:[关于使用相对 / 绝对路径](/guide/backend/relative-path.md)
|
||||
|
||||
### pageTitle
|
||||
|
||||
@@ -84,7 +85,7 @@ Artalk 支持多站点统一管理,此项用于站点隔离。
|
||||
|
||||
可以在后端的配置文件中定义前端的配置,让前端配置始终跟随后端。
|
||||
|
||||
详情可参考:[“在后端控制前端”](/guide/backend/fe-control)
|
||||
详情可参考:[在后端控制前端](/guide/backend/fe-control)
|
||||
|
||||
## 国际化 (i18n)
|
||||
|
||||
@@ -99,7 +100,7 @@ Artalk 支持多站点统一管理,此项用于站点隔离。
|
||||
|
||||
欢迎提交 PR 帮助翻译 Artalk 的多语言,为社区贡献一份力量!
|
||||
|
||||
详情参考:[“多语言”](./i18n.md)
|
||||
详情参考:[多语言](./i18n.md)
|
||||
|
||||
## 请求
|
||||
|
||||
@@ -121,7 +122,7 @@ Artalk 支持多站点统一管理,此项用于站点隔离。
|
||||
- 类型:`Object|Array|String|Boolean`
|
||||
- 默认值:"[https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json](https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json)"
|
||||
|
||||
详细内容:[“前端 · 表情包”](/guide/frontend/emoticons.md)
|
||||
详细内容:[前端 · 表情包](/guide/frontend/emoticons.md)
|
||||
|
||||
更新兼容 [OwO 格式](https://github.com/DIYgod/OwO),支持 URL 动态加载。
|
||||
|
||||
@@ -175,7 +176,7 @@ JS DELIVR 在中国大陆的 [ICP 牌照已被吊销](https://github.com/jsdeliv
|
||||
代码动态修改 darkMode:
|
||||
|
||||
```js
|
||||
Artalk.setDarkMode(true)
|
||||
artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
> 参考代码:“[index.html](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/index.html#L97-L150)”
|
||||
@@ -268,7 +269,7 @@ pvEl 和 countEl 元素标签都可以设置 `data-page-key` 属性值,来指
|
||||
**显示用户的 UserAgent 信息徽标**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
- 默认值:`false`
|
||||
|
||||
### listSort
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ frontend:
|
||||
|
||||
## 表情包预设
|
||||
|
||||
Artalk 社区提供许多表情包预设,你能够挑选几套喜欢的表情包,仅需简单的配置,轻松添加到你的评论系统中,前往仓库:[“@ArtalkJS/Emoticons”](https://github.com/ArtalkJS/Emoticons)。
|
||||
Artalk 社区提供许多表情包预设,你能够挑选几套喜欢的表情包,仅需简单的配置,轻松添加到你的评论系统中,前往仓库:[@ArtalkJS/Emoticons](https://github.com/ArtalkJS/Emoticons)。
|
||||
|
||||
## 格式支持
|
||||
|
||||
@@ -57,7 +57,7 @@ Artalk 除了支持 OwO 格式外,还内置支持一种标准的表情包列
|
||||
|
||||
::: warning
|
||||
|
||||
前端的配置将默认被后端覆盖,详情参考:[“在后端控制前端”](/guide/backend/fe-control.html)
|
||||
前端的配置将默认被后端覆盖,详情参考:[在后端控制前端](/guide/backend/fe-control.html)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
<<< @/code/quick-start/cdn.html
|
||||
|
||||
#### 什么时候执行 `Artalk.init({})`?
|
||||
|
||||
- 可以在任意位置引入 JS 和 CSS 资源,但需确保 JS 引入在执行 `Artalk.init({})` 前。
|
||||
- 执行 `Artalk.init({ el: '#artalk' })` 前需要确保 `<div id="artalk"></div>` 在页面当中。
|
||||
|
||||
## Hugo
|
||||
|
||||
创建模板文件 `/主题目录/layouts/partials/comment/artalk.html`:
|
||||
@@ -99,66 +104,36 @@ NexT 主题可以安装 [Hexo NexT 主题的 Artalk 插件](https://github.com/l
|
||||
|
||||
:::
|
||||
|
||||
## VitePress
|
||||
|
||||
可参考:[@ArtalkJS/Artalk:/docs/.vitepress](https://github.com/ArtalkJS/Artalk/tree/master/docs/.vitepress)
|
||||
|
||||
- `config.ts` 修改配置引入 CSS 资源
|
||||
- `theme/Artalk.vue` 创建组件
|
||||
- `theme/index.ts` 注册组件
|
||||
- `theme/Layout.vue` 使用组件
|
||||
|
||||
注:SSG 应用需通过 `import()` 函数异步引入 Artalk,否则会导致构建失败。
|
||||
|
||||
```ts
|
||||
import('artalk').then(({ default: Artalk }) => {
|
||||
artalk = Artalk.init({
|
||||
//...
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
参考文件:[.vitepress/theme/Artalk.vue](https://github.com/ArtalkJS/Artalk/blob/master/docs/.vitepress/theme/Artalk.vue)
|
||||
|
||||
## VuePress
|
||||
|
||||
以 [VuePress v2](https://github.com/vuepress/vuepress-next) 为例,继承默认主题 `@vuepress/theme-default`
|
||||
(待补充)
|
||||
|
||||
可参考:[“/.vuepress/theme/Artalk.vue”](https://github.com/ArtalkJS/Docs/blob/eef37bca8cc0c9973bf121fdef4014dcd940f104/docs/.vuepress/theme/Artalk.vue) 创建 Artalk 评论组件。
|
||||
::: tip 提示
|
||||
|
||||
在 `/.vuepress/theme/clientAppEnhance.ts` 文件中全局注册组件:
|
||||
可参考:[置入框架](./import-framework.md)
|
||||
|
||||
```ts
|
||||
import { defineClientAppEnhance } from '@vuepress/client'
|
||||
|
||||
import Artalk from './Artalk.vue'
|
||||
|
||||
export default defineClientAppEnhance(({ app, router, siteData }) => {
|
||||
app.component('Artalk', Artalk)
|
||||
})
|
||||
```
|
||||
|
||||
主题布局 `/.vuepress/theme/Layout.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Layout>
|
||||
<template #page-bottom>
|
||||
<div class="page-meta">
|
||||
<!-- Artalk -->
|
||||
<Artalk />
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Layout from '@vuepress/theme-default/lib/client/layouts/Layout.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { Layout },
|
||||
mounted: () => {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
主题配置文件 `/.vuepress/theme/index.ts`:
|
||||
|
||||
```ts
|
||||
import { path } from '@vuepress/utils'
|
||||
|
||||
export default ({
|
||||
name: 'vuepress-theme-local',
|
||||
extends: '@vuepress/theme-default',
|
||||
layouts: {
|
||||
Layout: path.resolve(__dirname, 'Layout.vue'),
|
||||
},
|
||||
clientAppEnhanceFiles: path.resolve(__dirname, 'clientAppEnhance.ts'),
|
||||
})
|
||||
```
|
||||
:::
|
||||
|
||||
## Typecho
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 置入框架
|
||||
|
||||
## 引入 Artalk
|
||||
## 安装 Artalk
|
||||
|
||||
通过包管理工具引入 Artalk,推荐使用 [pnpm](https://pnpm.io/zh/)
|
||||
|
||||
@@ -14,49 +14,84 @@ Vue 3 + TypeScript 例:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="artalk-comments"></div>
|
||||
<view ref="el"></view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
import { defineComponent } from 'vue'
|
||||
import Artalk from 'artalk'
|
||||
|
||||
export default defineComponent({
|
||||
mounted: () => {
|
||||
const artalk = Artalk.init({
|
||||
el: this.$el,
|
||||
pageKey: `${location.pathname}`,
|
||||
pageTitle: `${document.title}`,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk 的博客',
|
||||
// ...
|
||||
})
|
||||
}
|
||||
const el = ref<HTMLElement>()
|
||||
const route = useRoute()
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
pageKey: route.path,
|
||||
pageTitle: `${document.title}`,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk 的博客',
|
||||
// ...
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
VuePress 可参考:[“VuePress 引入”](./import-blog.md#vuepress)
|
||||
|
||||
:::
|
||||
|
||||
## React
|
||||
|
||||
```jsx
|
||||
import 'artalk/dist/Artalk.css'
|
||||
```tsx
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import 'artalk/dist/Artalk.css';
|
||||
import Artalk from 'artalk';
|
||||
|
||||
const ArtalkComment = () => {
|
||||
const el = useRef(null);
|
||||
const location = useLocation();
|
||||
let artalk = null;
|
||||
|
||||
useEffect(() => {
|
||||
artalk = new Artalk({
|
||||
el: el.current,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk 的博客',
|
||||
// ...
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (artalk) {
|
||||
artalk.destroy();
|
||||
}
|
||||
};
|
||||
}, [location.pathname]);
|
||||
|
||||
return <div ref={el}></div>;
|
||||
};
|
||||
|
||||
export default ArtalkComment;
|
||||
```
|
||||
|
||||
```jsx
|
||||
import React, { createRef } from 'react'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
import Artalk from 'artalk'
|
||||
|
||||
export default class Artalk extends React.Component {
|
||||
el = createRef()
|
||||
artalk = null
|
||||
|
||||
componentDidMount () {
|
||||
const artalk = Artalk.init({
|
||||
componentDidMount() {
|
||||
this.artalk = Artalk.init({
|
||||
el: this.el.current,
|
||||
pageKey: `${location.pathname}`,
|
||||
pageTitle: `${document.title}`,
|
||||
@@ -66,9 +101,13 @@ export default class Artalk extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
componentWillUnmount() {
|
||||
this.artalk?.destroy()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref={this.el} class="artalk-comments" />
|
||||
<div ref={this.el} />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -78,22 +117,30 @@ export default class Artalk extends React.Component {
|
||||
|
||||
```html
|
||||
<script>
|
||||
import 'artalk/dist/Artalk.css'
|
||||
import Artalk from 'artalk'
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import 'artalk/dist/Artalk.css';
|
||||
import Artalk from 'artalk';
|
||||
|
||||
let comments;
|
||||
let el;
|
||||
let artalk;
|
||||
|
||||
onMount(() => {
|
||||
Artalk.init({
|
||||
el: comments,
|
||||
pageKey: `${location.pathname}`,
|
||||
pageTitle: `${document.title}`,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk 的博客',
|
||||
// ...
|
||||
})
|
||||
})
|
||||
onMount(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk 的博客',
|
||||
// ...
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (artalk) {
|
||||
artalk.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={comments} class="artalk-comments"></div>
|
||||
<div bind:this={el}></div>
|
||||
```
|
||||
@@ -4,26 +4,4 @@ Artalk 拥有一个前端,[GitHub 代码仓库](https://github.com/ArtalkJS/Ar
|
||||
|
||||
## 特性
|
||||
|
||||
可见:[“README.md · 特性”](https://github.com/ArtalkJS/Artalk#%E7%89%B9%E6%80%A7)
|
||||
|
||||
## 句话概括
|
||||
|
||||
- 轻量级 (~30kB)
|
||||
- 易上手 (防秃顶)
|
||||
- TypeScript & Vanilla (纯天然无添加 / 无依赖)
|
||||
|
||||
<!-- ## 长篇大论
|
||||
|
||||
Artalk 的前端是重磅戏,它采用 TypeScript + Vite 编写,原生纯净无添加,不依赖于任何前端框架。
|
||||
|
||||
它体小精悍,得益于我们使用原生 DOM 操作,Artalk 的文件体积十分小巧,功能却非常完整。
|
||||
|
||||
您可以随意将 Artalk 置入任何位置,与 Vue, React, Svelte 项目紧密友好地结合。
|
||||
|
||||
开发时,我们考虑到 Artalk 的业务拓展能力,留有许多向外部暴露的 hooks,有助于完成你的预期需求。
|
||||
|
||||
它同时编译成几个版本,例如 UMD 版能够兼顾多种前端环境下运行。
|
||||
|
||||
CSS 方面,我们采用 Shadow DOM 特性对样式进行隔离处理,不污染页面样式且不被污染。
|
||||
|
||||
Vite 这样的下一代开发工具,为开发者创造了一个良好的编程体验,编译和热加载速度飞起。 -->
|
||||
可见:[README.md · 特性](https://github.com/ArtalkJS/Artalk#%E7%89%B9%E6%80%A7)
|
||||
@@ -6,7 +6,7 @@ Artalk 内置页面浏览量统计功能,你可以在你的页面任意位置
|
||||
<span id="ArtalkPV"></span>
|
||||
```
|
||||
|
||||
当 Artalk 加载完毕时,该标签内容将修改为页面的浏览量计数。
|
||||
当 Artalk 评论列表加载完毕时,该标签内容将修改为页面的浏览量计数。
|
||||
|
||||
Artalk 加载需要时间,所以你可以给它一个占位字符:
|
||||
|
||||
@@ -14,7 +14,7 @@ Artalk 加载需要时间,所以你可以给它一个占位字符:
|
||||
<span id="ArtalkPV">加载中...</span>
|
||||
```
|
||||
|
||||
你也可以绑定使用其他的元素,修改 Artalk 配置项,例如:
|
||||
配置项 `pvEl` 的默认值是 `"#ArtalkPV"`,可修改将评论量加载到指定元素,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
@@ -22,9 +22,12 @@ Artalk.init({
|
||||
})
|
||||
```
|
||||
|
||||
### 显示多个页面的浏览量
|
||||
### 加载多个页面的浏览量
|
||||
|
||||
你能在除评论页面之外的任何页面,例如「文章列表」页,显示页面浏览量或评论数。
|
||||
|
||||
在非评论页,无需调用 `Artalk.init` 加载评论框 (会使页面浏览量 PV 数增加),仅调用 `loadCountWidget` 静态方法即可:
|
||||
|
||||
你能在除评论页面之外的任何页面,例如「文章列表」页,显示页面浏览量或评论数:
|
||||
|
||||
```js
|
||||
Artalk.loadCountWidget({
|
||||
@@ -35,8 +38,6 @@ Artalk.loadCountWidget({
|
||||
});
|
||||
```
|
||||
|
||||
在非评论页时,就无需再 Artalk.init 实例了 (否则页面浏览量 PV 数会无故增加),仅调用 `loadCountWidget` 静态方法即可。
|
||||
|
||||
然后你可以放置多个 `#ArtalkPV` 元素,通过属性 `data-page-key` 来指定需要查询的页面:
|
||||
|
||||
```html
|
||||
|
||||
@@ -40,4 +40,4 @@
|
||||
|
||||
## 设置
|
||||
|
||||
登陆管理员账户可进入到 “控制中心” 的图形设置界面,轻松地对 [“配置文件”](/guide/backend/config.md) 以及 [“前端的配置”](/guide/frontend/config.md) 进行修改,而无需手动编辑复杂的配置文件。
|
||||
登陆管理员账户可进入到 “控制中心” 的图形设置界面,轻松地对 [配置文件](/guide/backend/config.md) 以及 [前端的配置](/guide/frontend/config.md) 进行修改,而无需手动编辑复杂的配置文件。
|
||||
+2
-2
@@ -36,7 +36,7 @@
|
||||
|
||||
不论是 Vue、React、Svelte 的前端项目,还是 WordPress、Typecho、Hexo 等博客系统,都可以快速引入 Artalk,结合诸位的聪明才智,我们相信 Artalk 能够自如应对各种业务场景。
|
||||
|
||||
> 更多支持 / 计划的功能,详见:[“README.md”](https://github.com/ArtalkJS/Artalk#todos)。
|
||||
> 更多支持 / 计划的功能,详见:[README.md](https://github.com/ArtalkJS/Artalk#todos)。
|
||||
|
||||
## 用户体验
|
||||
|
||||
@@ -64,7 +64,7 @@ Artalk 社区是包容开放的社区,我们欢迎不同水平的人员帮助
|
||||
|
||||
“More action, less talk”,Artalk 社区不欢迎无意义的争论,我们希望社区成员和谐共处、为社区发展出谋划策。在提出问题前,你应当读过《[提问的智慧](https://lug.ustc.edu.cn/wiki/doc/smart-questions/)》,这可能决定了你最终是否能得到有用的回答。在表达观点前,你应当具备基本的礼仪,比如保持平和的态度、使用得体的语言,切忌恶语相向、冷嘲热讽、不尊重他人信仰和立场等。
|
||||
|
||||
我们作为开源精神的推崇者以及实践者,希望我们所创造的自由软件,都应该被自由的使用,自由的研究,自由的更改和自由的分享。本项目前后端代码使用 [MIT](https://github.com/ArtalkJS/Artalk/blob/master/LICENSE) 协议开源,文档请遵循知识共享许可协议 ([Creative Commons License](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh))。
|
||||
我们作为开源精神的推崇者以及实践者,希望我们所创造的自由软件,都应该被自由的使用,自由的研究,自由的更改和自由的分享。本项目主程序使用 [MIT](https://github.com/ArtalkJS/Artalk/blob/master/LICENSE) 协议开源,文档使用 [CC](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh) 许可协议。
|
||||
::: tip 立即为社区贡献力量?
|
||||
|
||||
- 浏览开发者资料([开发文档](../develop/index.md) / [CONTRIBUTING.md](https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md))
|
||||
|
||||
@@ -2,20 +2,6 @@
|
||||
|
||||
## 用户身份凭证存储
|
||||
|
||||
RESTful API 具有无状态 (Stateless) 这一特性,尽管 Artalk 的 API 并不属于 RESTful,但保证无状态这一特性能让 API 更灵活易用,相反使用 Session 会话技术在处理多站点、跨域等操作的时候尤为繁琐,对于普通用户来说,服务器配置也成为麻烦,不当的配置会让用户的安全受到威胁。
|
||||
|
||||
在各种权衡考虑之下,Artalk 目前还是依然使用 localStorage 保存用户凭证数据,因为使用 localStorage 能保证 API 的无状态性,也无需考虑因使用 Cookie 而带来的各种麻烦,而且使用 Cookie 并不能换来多大的安全性提升。无论是 Cookie 还是 localStorage 都存在有一些缺陷。
|
||||
|
||||
Cookie 有 CSRF 安全隐患,而无状态 API 不容易有。如果使用 Cookie,就需要在任何有身份验证的地方,额外考虑对 CSRF 安全问题的防范,虽然可以使用全局 CSRF_TOKEN,但这无疑增加了 API 和客户端的复杂程度,开发也很难保证永远不会遗漏一些细节。所以,我认为使用 Cookie 既引入了一些额外的安全隐患,并且程序的开发难度也变大了,为什么要这样折磨自己呢?
|
||||
|
||||
还有一点是 Cookie 这种被广泛用作判断用户身份状态的技术特别容易被跟踪滥用,尽管可信的现代浏览器有很多内部的安全策略,但如果用户操作系统安装了一些恶意流氓软件,拥有较高权限,你无法保证某些软件窃取 Cookie 不是轻而易举的事 (特指某些大厂)。
|
||||
|
||||
那,Cookie 有啥优点?使用 Cookie 的主要优点在于能防止客户端 (浏览器) 通过 JS 对用户凭证数据的「直接」访问,阻止恶意 XSS 攻击脚本「直接」窃取数据的可能性。注意,这里用了「直接」这一个修饰词。虽然使用 Cookie (httpOnly) 不能通过 JS 读取凭证数据,但 XSS 攻击脚本依然可以在客户端执行 fetch 代码向 API 发起请求,这些请求依然是携带了 Cookie 的,是具有用户身份的 API 访问。也就是说,使用 Cookie (httpOnly) 并不能从根源上解决安全问题,不管你是 Cookie 还是 localStorage,只要网站存在 XSS 漏洞,攻击者成功注入了 XSS 脚本,都会对用户的安全造成威胁。所以使用 Cookie (httpOnly) 防 XSS 我认为是个「伪命题」。
|
||||
|
||||
XSS 是属于非常严重的问题了。所以无论使用何种方式存放敏感数据,XSS 防范才是重中之重。
|
||||
|
||||
对于我们用户来说,不应随意引入不明的 JS 脚本;不应在不了解代码意图的情况下到浏览器控制台执行 JS 代码;不应随意点击来历不明的链接;尽量使用新开的隐身模式窗口打开不明链接。
|
||||
|
||||
Artalk 在努力保护用户的安全。参考 [OWASP 安全备忘录](https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html) 中提到的内容,Artalk 的开发对于来自用户输入的任何数据始终持以怀疑的态度,我们严格遵循以下准则防范 XSS 攻击:
|
||||
|
||||
- 不直接或间接地调用 `innerHTML` 输出未经处理的用户输入数据,而是使用 `innerText`。
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
## 数据行囊
|
||||
|
||||
数据行囊(Artrans)是 Artalk 持久化数据保存规范格式。
|
||||
|
||||
Artran = Art + Ran (艺术 + 奔跑)
|
||||
|
||||
即 “奔跑的艺术”,又称~~艺术之润~~ Ran 为 Run 的过去式,代表已经润了(
|
||||
数据行囊(Artrans = Art + Ran)是 Artalk 持久化数据保存规范格式。
|
||||
|
||||
::: details Artran 格式定义
|
||||
|
||||
@@ -61,7 +57,7 @@ Artran = Art + Ran (艺术 + 奔跑)
|
||||
|
||||
转换为 `.artrans` 格式的数据文件可以导入 Artalk:
|
||||
|
||||
- **前端导入**:你可在「[控制中心](./frontend/sidebar.md#控制中心)」找到「迁移」选项卡,并根据提示导入 Artrans。
|
||||
- **控制中心导入**:你可在「[控制中心](./frontend/sidebar.md#控制中心)」找到「迁移」选项卡,并根据提示导入 Artrans。
|
||||
- **命令行导入**:执行 `artalk import -h` 查阅帮助文档。
|
||||
|
||||
## 获取源数据
|
||||
@@ -213,4 +209,4 @@ artalk export | gzip -9 | ssh username@remote_ip "cat > ~/backup/artrans.gz"
|
||||
|
||||
目前已支持将 Typecho、WordPress、Valine、Waline、Disqus、Commento、Twikoo 等类型的数据转为 Artrans,但鉴于评论系统的多样性,虽然我们已经对上述类型数据做了适配,但仍然还有许多并未兼容。如果你恰巧正在使用未被适配的评论系统,你除了等待 Artalk 官方支持之外,还可以尝试了解 Artrans 数据格式后自主编写评论数据导入导出工具。如果你觉得自己的工具写得不错,我们十分乐意将其收录在内,让我们共同创造一个能够在不同评论系统之间自由切换的工具。
|
||||
|
||||
前往:[“Artransfer 迁移工具代码仓库”](https://github.com/ArtalkJS/Artransfer)
|
||||
前往:[Artransfer 迁移工具代码仓库](https://github.com/ArtalkJS/Artransfer)
|
||||
+1
-1
@@ -6,7 +6,7 @@ titleTemplate: 自托管评论系统
|
||||
|
||||
hero:
|
||||
name: Artalk
|
||||
text: The Selfhosted Comment System
|
||||
text: A Self-hosted Comment System
|
||||
tagline: 轻量、安全、易上手的自托管评论系统
|
||||
actions:
|
||||
- theme: brand
|
||||
|
||||
@@ -8,8 +8,8 @@ export const Slogan: React.FC = () => {
|
||||
<div className="slogan-inner">
|
||||
<div className="text">
|
||||
<Reveal>
|
||||
The <span className='highlight'>
|
||||
Selfhosted
|
||||
A <span className='highlight'>
|
||||
Self-hosted
|
||||
<svg className='line-wrap' width="361" height="55" viewBox="0 0 361 55" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path className='line' d="M6 49C39.3707 29.6297 79.037 17.9573 117.534 28.5185C123.875 30.2581 129.86 32.8008 135.527 36.1667C139.986 38.8153 144.204 42.404 149.501 43.037C159.524 44.2349 168.187 33.3778 176.108 28.6481C191.347 19.5481 210.215 16.0215 227.536 21.1296C239.243 24.5822 249.671 29.5512 261.991 30.9815C295.794 34.9057 323.045 15.5757 354 7" stroke="#BDCFFF" stroke-width="12" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
@@ -23,5 +23,8 @@
|
||||
"markdown-it-for-inline": "^0.1.1",
|
||||
"sass": "^1.67.0",
|
||||
"vitepress": "1.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"artalk": "^2.7.0"
|
||||
}
|
||||
}
|
||||
Generated
+51
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
artalk:
|
||||
specifier: ^2.7.0
|
||||
version: 2.7.0
|
||||
|
||||
devDependencies:
|
||||
'@redocly/cli':
|
||||
specifier: 1.1.0
|
||||
@@ -2011,6 +2016,10 @@ packages:
|
||||
- vue
|
||||
dev: true
|
||||
|
||||
/abortcontroller-polyfill@1.7.5:
|
||||
resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==}
|
||||
dev: false
|
||||
|
||||
/algoliasearch@4.20.0:
|
||||
resolution: {integrity: sha512-y+UHEjnOItoNy0bYO+WWmLWBlPwDjKHW6mNHrPi0NkuhpQOOEbrkwQH/wgKFDLh7qlKjzoKeiRtlpewDPDG23g==}
|
||||
dependencies:
|
||||
@@ -2065,6 +2074,19 @@ packages:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: true
|
||||
|
||||
/artalk@2.7.0:
|
||||
resolution: {integrity: sha512-eV25c1vw0GtGmobh3W2bCjlIKxEd+k3u7WQ0IgC1ULd9PYCdwRMKjN7K+W+qlkUMVm2Kf4ti0Luouu07aE3sIw==}
|
||||
dependencies:
|
||||
abortcontroller-polyfill: 1.7.5
|
||||
hanabi: 0.4.0
|
||||
insane: 2.6.2
|
||||
marked: 7.0.5
|
||||
dev: false
|
||||
|
||||
/assignment@2.0.0:
|
||||
resolution: {integrity: sha512-naMULXjtgCs9SVUEtyvJNt68aF18em7/W+dhbR59kbz9cXWPEvUkCun2tqlgqRPSqZaKPpqLc5ZnwL8jVmJRvw==}
|
||||
dev: false
|
||||
|
||||
/babel-plugin-polyfill-corejs2@0.4.5(@babel/core@7.22.20):
|
||||
resolution: {integrity: sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==}
|
||||
peerDependencies:
|
||||
@@ -2224,6 +2246,11 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/comment-regex@1.0.1:
|
||||
resolution: {integrity: sha512-IWlN//Yfby92tOIje7J18HkNmWRR7JESA/BK8W7wqY/akITpU5B0JQWnbTjCfdChSrDNb0DrdA9jfAxiiBXyiQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
@@ -2427,6 +2454,12 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/hanabi@0.4.0:
|
||||
resolution: {integrity: sha512-ixJH94fwmmVzUSdxl7TMkVZJmsq4d2JKrxedpM5V1V+91iVHL0q6NnJi4xiDahK6Vo00xT17H8H6b4F6RVbsOg==}
|
||||
dependencies:
|
||||
comment-regex: 1.0.1
|
||||
dev: false
|
||||
|
||||
/handlebars@4.7.8:
|
||||
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
|
||||
engines: {node: '>=0.4.7'}
|
||||
@@ -2452,6 +2485,11 @@ packages:
|
||||
function-bind: 1.1.1
|
||||
dev: true
|
||||
|
||||
/he@0.5.0:
|
||||
resolution: {integrity: sha512-DoufbNNOFzwRPy8uecq+j+VCPQ+JyDelHTmSgygrA5TsR8Cbw4Qcir5sGtWiusB4BdT89nmlaVDhSJOqC/33vw==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/http2-client@1.3.5:
|
||||
resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==}
|
||||
dev: true
|
||||
@@ -2471,6 +2509,13 @@ packages:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: true
|
||||
|
||||
/insane@2.6.2:
|
||||
resolution: {integrity: sha512-BqEL1CJsjJi+/C/zKZxv31zs3r6zkLH5Nz1WMFb7UBX2KHY2yXDpbFTSEmNHzomBbGDysIfkTX55A0mQZ2CQiw==}
|
||||
dependencies:
|
||||
assignment: 2.0.0
|
||||
he: 0.5.0
|
||||
dev: false
|
||||
|
||||
/is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2614,6 +2659,12 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/marked@7.0.5:
|
||||
resolution: {integrity: sha512-lwNAFTfXgqpt/XvK17a/8wY9/q6fcSPZT1aP6QW0u74VwaJF/Z9KbRcX23sWE4tODM+AolJNcUtErTkgOeFP/Q==}
|
||||
engines: {node: '>= 16'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
dependencies:
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@ extends:
|
||||
- recommended
|
||||
|
||||
apis:
|
||||
artalk@v1:
|
||||
artalk@v2:
|
||||
root: ./swagger/swagger.json
|
||||
rules: []
|
||||
rules: {}
|
||||
|
||||
theme:
|
||||
openapi:
|
||||
|
||||
Vendored
+38
-2
@@ -2,15 +2,29 @@ package simple_cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
data sync.Map
|
||||
// when the number of entries reaches gcThold, GC will be triggered
|
||||
gcThold int
|
||||
len atomic.Int32
|
||||
}
|
||||
|
||||
func New() *Cache {
|
||||
return &Cache{}
|
||||
return &Cache{
|
||||
gcThold: 1000,
|
||||
len: atomic.Int32{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewWithGCThold(gcThold int) *Cache {
|
||||
return &Cache{
|
||||
gcThold: gcThold,
|
||||
len: atomic.Int32{},
|
||||
}
|
||||
}
|
||||
|
||||
type cacheEntry struct {
|
||||
@@ -19,6 +33,7 @@ type cacheEntry struct {
|
||||
}
|
||||
|
||||
func (c *Cache) Set(key string, value interface{}, expirationParam ...time.Duration) {
|
||||
defer c.GC(false)
|
||||
var expirationTime time.Time
|
||||
if len(expirationParam) > 0 && expirationParam[0] > 0 {
|
||||
expirationTime = time.Now().Add(expirationParam[0])
|
||||
@@ -28,12 +43,14 @@ func (c *Cache) Set(key string, value interface{}, expirationParam ...time.Durat
|
||||
expiration: expirationTime,
|
||||
}
|
||||
c.data.Store(key, entry)
|
||||
c.len.Add(1)
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) (interface{}, bool) {
|
||||
defer c.GC(false)
|
||||
if val, found := c.data.Load(key); found {
|
||||
entry := val.(cacheEntry)
|
||||
if entry.expiration.IsZero() || entry.expiration.After(time.Now()) {
|
||||
if !isExpired(entry) {
|
||||
return entry.value, true
|
||||
}
|
||||
c.data.Delete(key)
|
||||
@@ -43,4 +60,23 @@ func (c *Cache) Get(key string) (interface{}, bool) {
|
||||
|
||||
func (c *Cache) Delete(key string) {
|
||||
c.data.Delete(key)
|
||||
c.len.Add(-1)
|
||||
}
|
||||
|
||||
func (c* Cache) GC(force bool) {
|
||||
if int(c.len.Load()) < c.gcThold && !force {
|
||||
return
|
||||
}
|
||||
c.data.Range(func(key, value interface{}) bool {
|
||||
entry := value.(cacheEntry)
|
||||
if !isExpired(entry) {
|
||||
return true
|
||||
}
|
||||
c.Delete(key.(string))
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func isExpired(entry cacheEntry) bool {
|
||||
return !entry.expiration.IsZero() && entry.expiration.Before(time.Now())
|
||||
}
|
||||
+43
@@ -60,4 +60,47 @@ func TestCache(t *testing.T) {
|
||||
t.Error("cache should be deleted")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GC thold reached", func(t *testing.T) {
|
||||
cache := NewWithGCThold(1)
|
||||
cache.Set("key", "value", 1*time.Millisecond)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
cache.GC(false)
|
||||
_, ok := cache.Get("key")
|
||||
if ok {
|
||||
t.Error("cache should be expired")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GC not expired", func(t *testing.T) {
|
||||
cache := NewWithGCThold(1)
|
||||
cache.Set("key", "value")
|
||||
cache.GC(false)
|
||||
_, ok := cache.Get("key")
|
||||
if !ok {
|
||||
t.Error("cache should not be expired")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GC thold not reached", func(t *testing.T) {
|
||||
cache := NewWithGCThold(2)
|
||||
cache.Set("key", "value", 1*time.Millisecond)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
cache.GC(false)
|
||||
len := cache.len.Load()
|
||||
if len != 1 {
|
||||
t.Errorf("cache length should be 1, but got %d", len)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GC thold not reached but force GC", func(t *testing.T) {
|
||||
cache := NewWithGCThold(2)
|
||||
cache.Set("key", "value", 1*time.Millisecond)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
cache.GC(true)
|
||||
_, ok := cache.Get("key")
|
||||
if ok {
|
||||
t.Error("cache should be expired")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -150,6 +150,21 @@ func (conf *Config) i18nPatch() {
|
||||
} else if conf.Locale == "zh" {
|
||||
conf.Locale = "zh-CN"
|
||||
}
|
||||
|
||||
// Case Convert
|
||||
// follow Unicode BCP 47
|
||||
// @see https://www.techonthenet.com/js/language_tags.php
|
||||
parts := strings.Split(conf.Locale, "-")
|
||||
if len(parts) == 2 {
|
||||
conf.Locale = strings.ToLower(parts[0]) + "-" + strings.ToUpper(parts[1])
|
||||
} else if len(parts) == 1 {
|
||||
conf.Locale = strings.ToLower(parts[0])
|
||||
}
|
||||
|
||||
// Temporary convert `en-*` to `en`
|
||||
if parts[0] == "en" {
|
||||
conf.Locale = "en"
|
||||
}
|
||||
}
|
||||
|
||||
// IP属地功能配置修补
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestI18nPatch(t *testing.T) {
|
||||
test := func(input string, expected string) {
|
||||
t.Run("locale="+input, func(t *testing.T) {
|
||||
config := &Config{Locale: input}
|
||||
config.i18nPatch()
|
||||
assert.Equal(t, expected, config.Locale)
|
||||
})
|
||||
}
|
||||
|
||||
test("", "en")
|
||||
test("en", "en")
|
||||
test("en-US", "en")
|
||||
|
||||
test("zh", "zh-CN")
|
||||
test("zh-CN", "zh-CN")
|
||||
|
||||
t.Run("case convert", func(t *testing.T) {
|
||||
// @see https://www.techonthenet.com/js/language_tags.php
|
||||
test("zh-cn", "zh-CN")
|
||||
test("ZH-cn", "zh-CN")
|
||||
test("EN", "en")
|
||||
})
|
||||
}
|
||||
@@ -16,7 +16,13 @@ func (pusher *NotifyPusher) multiPush(comment *entity.Comment, pComment *entity.
|
||||
}
|
||||
|
||||
// 通知消息
|
||||
firstAdminUser := pusher.dao.GetAllAdminIDs()[0]
|
||||
allAdmins := pusher.dao.GetAllAdminIDs()
|
||||
if len(allAdmins) == 0 {
|
||||
log.Warn("Multi push disabled: no admin user found")
|
||||
return
|
||||
}
|
||||
|
||||
firstAdminUser := allAdmins[0]
|
||||
subject, body := pusher.getAdminNotifySubjectBody(comment, firstAdminUser)
|
||||
|
||||
log.Debug(time.Now(), " 多元推送")
|
||||
|
||||
@@ -11,8 +11,8 @@ then
|
||||
export VOLTA_HOME="${HOME}/.volta"
|
||||
export PATH="${VOLTA_HOME}/bin:${PATH}"
|
||||
|
||||
volta install node@18.17.1
|
||||
volta install pnpm@8.7.0
|
||||
volta install node@18.19.0
|
||||
volta install pnpm@8.12.1
|
||||
fi
|
||||
|
||||
pnpm --dir ./ui install --frozen-lockfile
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Check if `data/ui_test.db` exits
|
||||
if [ ! -f data/ui_test.tpl.db ]; then
|
||||
echo "data/ui_test.tpl.db not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy `data/ui_test.tpl.db` to `data/ui_test.db`
|
||||
cp data/ui_test.tpl.db data/ui_test.db
|
||||
|
||||
# Check if yq is installed
|
||||
if ! [ -x "$(command -v yq)" ]; then
|
||||
echo -e 'Error: yq is not installed. \nSee: https://github.com/mikefarah/yq#install' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Modify `artalk.yml` db.file to `data/ui_test.db` use yq
|
||||
yq -i '.db.file = "data/ui_test.db"' artalk.yml
|
||||
|
||||
# Run frontend e2e test
|
||||
pnpm -C ui/packages/artalk run test:e2e
|
||||
|
||||
# if args contains `--show-report` then open report
|
||||
if [[ $* == *--show-report* ]]; then
|
||||
pnpm -C ui/packages/artalk run test:report
|
||||
fi
|
||||
@@ -46,5 +46,6 @@ func GetApiPublicConfDataMap(app *core.App, c *fiber.Ctx) Map {
|
||||
return Map{
|
||||
"img_upload": imgUpload,
|
||||
"frontend_conf": frontendConf,
|
||||
"version": GetApiVersionDataMap(),
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,15 @@ func ImgUpload(app *core.App, router fiber.Router) {
|
||||
if baseURL == "" {
|
||||
baseURL = config.IMG_UPLOAD_PUBLIC_PATH
|
||||
}
|
||||
imgURL := path.Join(baseURL, filename)
|
||||
|
||||
var imgURL string
|
||||
if utils.ValidateURL(baseURL) {
|
||||
// full url
|
||||
imgURL = strings.TrimSuffix(baseURL, "/") + "/" + filename
|
||||
} else {
|
||||
// relative path
|
||||
imgURL = path.Join(baseURL, filename)
|
||||
}
|
||||
|
||||
// 使用 upgit
|
||||
if app.Conf().ImgUpload.Upgit.Enabled {
|
||||
|
||||
+3
-3
@@ -39,8 +39,8 @@ func NewTestApp() (*TestApp, error) {
|
||||
}
|
||||
|
||||
// load assets fs
|
||||
dir := os.DirFS("./")
|
||||
pkged.SetFS(dir)
|
||||
dirFS := os.DirFS("./")
|
||||
pkged.SetFS(dirFS)
|
||||
|
||||
// prepare db folder
|
||||
const dbFile = "./data/test.db"
|
||||
@@ -65,7 +65,7 @@ func NewTestApp() (*TestApp, error) {
|
||||
|
||||
// fixtures config
|
||||
fixtures, err := testfixtures.New(
|
||||
testfixtures.Database(sqlDB), // You database connection
|
||||
testfixtures.Database(sqlDB), // Database connection
|
||||
testfixtures.Dialect("sqlite"), // Available: "postgresql", "timescaledb", "mysql", "mariadb", "sqlite" and "sqlserver"
|
||||
testfixtures.Directory("./test/fixtures"), // The directory containing the YAML files
|
||||
)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/.vitepress/dist/
|
||||
/.vitepress/.temp/
|
||||
/.vitepress/cache/
|
||||
/public/http-api.html
|
||||
@@ -0,0 +1,28 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "My Awesome Project",
|
||||
description: "A VitePress Site",
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Examples', link: '/markdown-examples' }
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: 'Examples',
|
||||
items: [
|
||||
{ text: 'Markdown Examples', link: '/markdown-examples' },
|
||||
{ text: 'Runtime API Examples', link: '/api-examples' }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div ref="el" style="margin-top: 20px;"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, nextTick, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
import Artalk from 'artalk'
|
||||
import 'artalk/dist/artalk.css'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
console.log(ctx)
|
||||
})
|
||||
|
||||
const el = ref<HTMLElement>()
|
||||
|
||||
const router = useRouter()
|
||||
const page = useData().page
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initArtalk(getConfByPage())
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => router.route.path, () => {
|
||||
nextTick(() => {
|
||||
artalk.update(getConfByPage())
|
||||
artalk.reload()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
|
||||
function initArtalk(conf: any) {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
gravatar: {
|
||||
mirror: 'https://cravatar.cn/avatar/'
|
||||
},
|
||||
...conf
|
||||
})
|
||||
|
||||
loadExtraFuncs()
|
||||
}
|
||||
|
||||
function getConfByPage() {
|
||||
return {
|
||||
pageKey: 'https://artalk.js.org'+router.route.path,
|
||||
pageTitle: page.value.title,
|
||||
server: 'http://localhost:23366',
|
||||
site: 'ArtalkDocs',
|
||||
}
|
||||
}
|
||||
|
||||
function loadExtraFuncs() {
|
||||
// 夜间模式
|
||||
const darkMode = document.querySelector('html')!.classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
|
||||
new MutationObserver((mList) => {
|
||||
mList.forEach((m) => {
|
||||
if (m.attributeName !== 'class') return
|
||||
|
||||
// @ts-ignore
|
||||
const darkMode = m.target.classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html')!, { attributes: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue'
|
||||
import Artalk from './Artalk.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Artalk />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script setup>
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { onMounted } from 'vue'
|
||||
import Artalk from './Artalk.vue'
|
||||
import './style.scss'
|
||||
|
||||
const { Layout } = DefaultTheme
|
||||
|
||||
onMounted(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #doc-after>
|
||||
<Artalk />
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
@@ -0,0 +1,19 @@
|
||||
// https://vitepress.dev/guide/custom-theme
|
||||
import { h } from 'vue'
|
||||
import type { Theme } from 'vitepress'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import Artalk from './Artalk.vue'
|
||||
import './style.css'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout: () => {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'doc-after': () => h(Artalk)
|
||||
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
||||
})
|
||||
},
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// ...
|
||||
}
|
||||
} satisfies Theme
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Customize default theme styling by overriding CSS variables:
|
||||
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
|
||||
*/
|
||||
|
||||
/**
|
||||
* Colors
|
||||
*
|
||||
* Each colors have exact same color scale system with 3 levels of solid
|
||||
* colors with different brightness, and 1 soft color.
|
||||
*
|
||||
* - `XXX-1`: The most solid color used mainly for colored text. It must
|
||||
* satisfy the contrast ratio against when used on top of `XXX-soft`.
|
||||
*
|
||||
* - `XXX-2`: The color used mainly for hover state of the button.
|
||||
*
|
||||
* - `XXX-3`: The color for solid background, such as bg color of the button.
|
||||
* It must satisfy the contrast ratio with pure white (#ffffff) text on
|
||||
* top of it.
|
||||
*
|
||||
* - `XXX-soft`: The color used for subtle background such as custom container
|
||||
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
|
||||
* on top of it.
|
||||
*
|
||||
* The soft color must be semi transparent alpha channel. This is crucial
|
||||
* because it allows adding multiple "soft" colors on top of each other
|
||||
* to create a accent, such as when having inline code block inside
|
||||
* custom containers.
|
||||
*
|
||||
* - `default`: The color used purely for subtle indication without any
|
||||
* special meanings attched to it such as bg color for menu hover state.
|
||||
*
|
||||
* - `brand`: Used for primary brand colors, such as link text, button with
|
||||
* brand theme, etc.
|
||||
*
|
||||
* - `tip`: Used to indicate useful information. The default theme uses the
|
||||
* brand color for this by default.
|
||||
*
|
||||
* - `warning`: Used to indicate warning to the users. Used in custom
|
||||
* container, badges, etc.
|
||||
*
|
||||
* - `danger`: Used to show error, or dangerous message to the users. Used
|
||||
* in custom container, badges, etc.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-default-1: var(--vp-c-gray-1);
|
||||
--vp-c-default-2: var(--vp-c-gray-2);
|
||||
--vp-c-default-3: var(--vp-c-gray-3);
|
||||
--vp-c-default-soft: var(--vp-c-gray-soft);
|
||||
|
||||
--vp-c-brand-1: var(--vp-c-indigo-1);
|
||||
--vp-c-brand-2: var(--vp-c-indigo-2);
|
||||
--vp-c-brand-3: var(--vp-c-indigo-3);
|
||||
--vp-c-brand-soft: var(--vp-c-indigo-soft);
|
||||
|
||||
--vp-c-tip-1: var(--vp-c-brand-1);
|
||||
--vp-c-tip-2: var(--vp-c-brand-2);
|
||||
--vp-c-tip-3: var(--vp-c-brand-3);
|
||||
--vp-c-tip-soft: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-c-warning-1: var(--vp-c-yellow-1);
|
||||
--vp-c-warning-2: var(--vp-c-yellow-2);
|
||||
--vp-c-warning-3: var(--vp-c-yellow-3);
|
||||
--vp-c-warning-soft: var(--vp-c-yellow-soft);
|
||||
|
||||
--vp-c-danger-1: var(--vp-c-red-1);
|
||||
--vp-c-danger-2: var(--vp-c-red-2);
|
||||
--vp-c-danger-3: var(--vp-c-red-3);
|
||||
--vp-c-danger-soft: var(--vp-c-red-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Button
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-button-brand-border: transparent;
|
||||
--vp-button-brand-text: var(--vp-c-white);
|
||||
--vp-button-brand-bg: var(--vp-c-brand-3);
|
||||
--vp-button-brand-hover-border: transparent;
|
||||
--vp-button-brand-hover-text: var(--vp-c-white);
|
||||
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
|
||||
--vp-button-brand-active-border: transparent;
|
||||
--vp-button-brand-active-text: var(--vp-c-white);
|
||||
--vp-button-brand-active-bg: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Home
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(
|
||||
120deg,
|
||||
#bd34fe 30%,
|
||||
#41d1ff
|
||||
);
|
||||
|
||||
--vp-home-hero-image-background-image: linear-gradient(
|
||||
-45deg,
|
||||
#bd34fe 50%,
|
||||
#47caff 50%
|
||||
);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Custom Block
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-custom-block-tip-border: transparent;
|
||||
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
||||
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Algolia
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
.DocSearch {
|
||||
--docsearch-primary-color: var(--vp-c-brand-1) !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Runtime API Examples
|
||||
|
||||
This page demonstrates usage of some of the runtime APIs provided by VitePress.
|
||||
|
||||
The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
|
||||
|
||||
```md
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { theme, page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
## Results
|
||||
|
||||
### Theme Data
|
||||
<pre>{{ theme }}</pre>
|
||||
|
||||
### Page Data
|
||||
<pre>{{ page }}</pre>
|
||||
|
||||
### Page Frontmatter
|
||||
<pre>{{ frontmatter }}</pre>
|
||||
```
|
||||
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { site, theme, page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
## Results
|
||||
|
||||
### Theme Data
|
||||
<pre>{{ theme }}</pre>
|
||||
|
||||
### Page Data
|
||||
<pre>{{ page }}</pre>
|
||||
|
||||
### Page Frontmatter
|
||||
<pre>{{ frontmatter }}</pre>
|
||||
|
||||
## More
|
||||
|
||||
Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "My Awesome Project"
|
||||
text: "A VitePress Site"
|
||||
tagline: My great project tagline
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Markdown Examples
|
||||
link: /markdown-examples
|
||||
- theme: alt
|
||||
text: API Examples
|
||||
link: /api-examples
|
||||
|
||||
features:
|
||||
- title: Feature A
|
||||
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
||||
- title: Feature B
|
||||
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
||||
- title: Feature C
|
||||
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
||||
---
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Markdown Extension Examples
|
||||
|
||||
This page demonstrates some of the built-in markdown extensions provided by VitePress.
|
||||
|
||||
## Syntax Highlighting
|
||||
|
||||
VitePress provides Syntax Highlighting powered by [Shikiji](https://github.com/antfu/shikiji), with additional features like line-highlighting:
|
||||
|
||||
**Input**
|
||||
|
||||
````md
|
||||
```js{4}
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Highlighted!'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
**Output**
|
||||
|
||||
```js{4}
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Highlighted!'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Containers
|
||||
|
||||
**Input**
|
||||
|
||||
```md
|
||||
::: info
|
||||
This is an info box.
|
||||
:::
|
||||
|
||||
::: tip
|
||||
This is a tip.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
This is a warning.
|
||||
:::
|
||||
|
||||
::: danger
|
||||
This is a dangerous warning.
|
||||
:::
|
||||
|
||||
::: details
|
||||
This is a details block.
|
||||
:::
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
::: info
|
||||
This is an info box.
|
||||
:::
|
||||
|
||||
::: tip
|
||||
This is a tip.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
This is a warning.
|
||||
:::
|
||||
|
||||
::: danger
|
||||
This is a dangerous warning.
|
||||
:::
|
||||
|
||||
::: details
|
||||
This is a details block.
|
||||
:::
|
||||
|
||||
## More
|
||||
|
||||
Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "ssg-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"docs:dev": "vitepress dev",
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"vitepress": "1.0.0-rc.32"
|
||||
},
|
||||
"dependencies": {
|
||||
"artalk": "link:ui/packages/artalk",
|
||||
"vue": "^3.3.12"
|
||||
}
|
||||
}
|
||||
Generated
+1249
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# vue
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "vue",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build --force"
|
||||
},
|
||||
"dependencies": {
|
||||
"artalk": "link:artalk",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/node": "^18.19.3",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"@vue/tsconfig": "^0.5.0",
|
||||
"npm-run-all2": "^6.1.1",
|
||||
"typescript": "~5.3.0",
|
||||
"vite": "^5.0.10",
|
||||
"vue-tsc": "^1.8.25"
|
||||
}
|
||||
}
|
||||
Generated
+1012
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<div class="wrapper">
|
||||
<HelloWorld msg="Artalk Vue Route Testing" />
|
||||
|
||||
<nav>
|
||||
<RouterLink to="/">Home</RouterLink>
|
||||
<RouterLink to="/about">About</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import Artalk from 'artalk'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
const el = ref<HTMLElement>()
|
||||
const route = useRoute()
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
server: 'http://localhost:23366',
|
||||
site: 'ArtalkDocs',
|
||||
pageKey: route.path,
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view ref="el"></view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
msg: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,23 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('../views/AboutView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import Artalk from '@/components/Artalk.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
<Artalk />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import Artalk from '@/components/Artalk.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
Hello Home
|
||||
<Artalk />
|
||||
</main>
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
|
||||
vite*.config*
|
||||
|
||||
# 不检测 js
|
||||
**/*.js
|
||||
+17
-14
@@ -1,7 +1,9 @@
|
||||
const path = require('node:path')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
env: { "browser": true },
|
||||
parser: '@typescript-eslint/parser',
|
||||
env: { browser: true },
|
||||
parserOptions: {
|
||||
project: ['./packages/*/tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
@@ -12,37 +14,38 @@ module.exports = {
|
||||
'prettier',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
"plugin:compat/recommended",
|
||||
'plugin:compat/recommended',
|
||||
],
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
plugins: ['prettier', '@typescript-eslint', 'import'],
|
||||
ignorePatterns: ['node_modules/', 'coverage/', 'dist/', '**/*.js'],
|
||||
rules: {
|
||||
'no-alert': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-alert': 'warn',
|
||||
'no-unused-vars': 0,
|
||||
'no-plusplus': 0,
|
||||
'no-param-reassign': 0,
|
||||
'no-console': 0,
|
||||
'no-underscore-dangle': 0,
|
||||
'no-constructor-return': 0,
|
||||
'class-methods-use-this': 0,
|
||||
'spaced-comment': 0,
|
||||
'no-lonely-if': 0,
|
||||
'prefer-destructuring': 0,
|
||||
'import/no-cycle': 0,
|
||||
'import/prefer-default-export': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'@typescript-eslint/lines-between-class-members': 0,
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 0,
|
||||
'@typescript-eslint/naming-convention': 0,
|
||||
'@typescript-eslint/no-useless-constructor': 0,
|
||||
'@typescript-eslint/no-unused-expressions': 0, // for `func && func()` expressions
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
"project": [__dirname + "/packages/*/tsconfig.json"]
|
||||
project: ['packages/artalk/tsconfig.json'].map((p) =>
|
||||
path.resolve(__dirname, p),
|
||||
),
|
||||
},
|
||||
},
|
||||
'polyfills': [
|
||||
'AbortController'
|
||||
]
|
||||
polyfills: ['AbortController'],
|
||||
},
|
||||
// @see https://stackoverflow.com/questions/63118405/how-to-fix-eslintrc-the-file-does-not-match-your-project-config
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.history
|
||||
.editorconfig
|
||||
|
||||
# git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# tests
|
||||
test
|
||||
coverage
|
||||
|
||||
# build tools
|
||||
.travis.yml
|
||||
.jenkins.yml
|
||||
.codeclimate.yml
|
||||
|
||||
# linters
|
||||
.jscsrc
|
||||
.jshintrc
|
||||
.eslintrc*
|
||||
|
||||
# vite configs
|
||||
vite*.config*
|
||||
|
||||
# develop tool configs
|
||||
.prettierrc
|
||||
.eslintrc.js
|
||||
.eslintignore
|
||||
postcss.config.js
|
||||
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
# local files
|
||||
deploy
|
||||
local
|
||||
CNAME
|
||||
index.html
|
||||
#dist
|
||||
|
||||
.github
|
||||
@@ -0,0 +1,11 @@
|
||||
# dist files
|
||||
dist/
|
||||
|
||||
# node modules
|
||||
node_modules/
|
||||
|
||||
# pnpm lock file
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Minified files
|
||||
artalk-sidebar/src/lib/md5.js
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"singleQuote": true,
|
||||
"htmlWhitespaceSensitivity": "ignore"
|
||||
}
|
||||
Loaded 100 of 333 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user