Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
296cfde58a | ||
|
|
f88631ad53 | ||
|
|
650d38b1dd | ||
|
|
bf7878eabb | ||
|
|
e3fa83806e | ||
|
|
1a0aca42fa | ||
|
|
7983658292 | ||
|
|
0a5714139f | ||
|
|
8620420fb4 |
No files matched your search
+19
-16
@@ -1,28 +1,31 @@
|
||||
name: NPM Package
|
||||
# https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
||||
name: NPM Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: github.repository == "ArtalkJS/Artalk"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm install
|
||||
- run: npm build
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
# Setup .npmrc file to publish to GitHub Packages
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
- name: Release
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
npx version-from-git --allow-same-version --template 'master.short'
|
||||
- name: Publish
|
||||
run: |
|
||||
npm ci
|
||||
npm publish
|
||||
# Publish to GitHub Packages
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
# Artalk
|
||||
|
||||
> 🌌 一款简洁的自托管评论系统 | A Selfhosted Comment System.
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://github.com/ArtalkJS/Artalk/commits/master)
|
||||
@@ -14,7 +12,11 @@
|
||||
[](https://travis-ci.com/ArtalkJS/Artalk)
|
||||
[](./LICENSE)
|
||||
|
||||
## [官网前往](https://artalk.js.org) | [文档细节](https://artalk.js.org/docs)
|
||||
> 🌌 一款简洁的自托管评论系统 | A Selfhosted Comment System.
|
||||
|
||||
前往:[“**官方文档**”](https://artalk.js.org)
|
||||
|
||||
---
|
||||
|
||||
- 轻量 (~30kB gzipped)
|
||||
- 自托管 (隐私安全)
|
||||
@@ -22,7 +24,7 @@
|
||||
- Markdown 语法 (通用性)
|
||||
- Golang 后端 (易部署 / 跨平台)
|
||||
|
||||
## 特色功能
|
||||
## 特性
|
||||
|
||||
- 表 情 包 :统一管理 / 链接替换
|
||||
- 通知中心:红点标记 / 已读记录
|
||||
@@ -46,11 +48,16 @@
|
||||
- TypeScript & Vanilla:纯天然无添加 / 无需依赖
|
||||
- [Vite](https://github.com/vitejs/vite):开发者的极致体验
|
||||
|
||||
## 基本食用方针
|
||||
## 食用方针
|
||||
|
||||
前往:[“**文档 · 部署**”](https://artalk.js.org/guide/deploy.html)
|
||||
|
||||
### 两步抵达
|
||||
|
||||
1. 后端程序部署:[ArtalkGo](https://github.com/ArtalkJS/ArtalkGo)
|
||||
<details>
|
||||
<summary>点击展开</summary>
|
||||
|
||||
1. 后端程序部署,前往:[“**文档 · 后端部署**”](https://artalk.js.org/guide/backend/install.html)
|
||||
2. 前端页面引入:
|
||||
|
||||
```html
|
||||
@@ -65,29 +72,26 @@
|
||||
<body>
|
||||
|
||||
<!-- 给定元素 -->
|
||||
<div id="ArtalkComments"></div>
|
||||
<div id="Comments"></div>
|
||||
|
||||
<!-- 引入 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/artalk@2/dist/Artalk.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/artalk@2/dist/Artalk.js"></script>
|
||||
|
||||
<!-- 配置 Artalk -->
|
||||
<script>
|
||||
new Artalk({
|
||||
el: '#ArtalkComments',
|
||||
placeholder: '说点什么 (づ ̄ 3 ̄)づ',
|
||||
noComment: '此时无声胜有声',
|
||||
pageKey: '', // 页面链接 (填写一个页面的完整 URL)
|
||||
pageTitle: '', // 页面标题 (用于后台列表显示)
|
||||
server: '', // 后端程序 (填写后端程序完整 URL;例如:https://domain/api)
|
||||
site: '', // 站点名称(用于站点隔离,留空使用默认站点)
|
||||
el: '#Comments',
|
||||
pageKey: '<页面链接>',
|
||||
pageTitle: '<页面标题>',
|
||||
server: '<后端地址>',
|
||||
site: '<站点名称>',
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
更多细节请 [查阅文档](https://artalk.js.org/docs)
|
||||
</details>
|
||||
|
||||
### CDN 资源
|
||||
|
||||
@@ -98,7 +102,7 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/artalk@2/dist/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/artalk@2/dist/Artalk.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/artalk@2/dist/Artalk.js"></script>
|
||||
```
|
||||
|
||||
### NPM 引用
|
||||
@@ -111,7 +115,7 @@ $ yarn add artalk
|
||||
import Artalk from 'artalk'
|
||||
|
||||
new Artalk({
|
||||
// ...your configs
|
||||
// ...your config
|
||||
})
|
||||
```
|
||||
|
||||
@@ -130,4 +134,4 @@ Artalk 拥有 TypeScript 声明文件,能够在引用时帮助您进行代码
|
||||
[GPL-3.0](./LICENSE)
|
||||
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_large)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_large)
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "artalk",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"description": "A Fast, Slight & Delightful Comment",
|
||||
"keywords": [
|
||||
"comment-system",
|
||||
@@ -16,7 +16,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/ArtalkJS/Artalk.git"
|
||||
},
|
||||
"main": "./dist/Artalk.umd.js",
|
||||
"main": "./dist/Artalk.js",
|
||||
"unpkg": "./dist/Artalk.iife.js",
|
||||
"jsdelivr": "./dist/Artalk.iife.js",
|
||||
"module": "./dist/Artalk.es.js",
|
||||
@@ -24,7 +24,7 @@
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/Artalk.es.js",
|
||||
"require": "./dist/Artalk.umd.js"
|
||||
"require": "./dist/Artalk.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -357,57 +357,137 @@ export default class AdminView extends SidebarView {
|
||||
|
||||
const settingEl = Utils.createElement(`
|
||||
<div class="atk-setting">
|
||||
<div class="atk-log-wrap" style="display: none;">
|
||||
<div class="atk-log-back-btn">返回</div>
|
||||
<div class="atk-log"></div>
|
||||
</div>
|
||||
<div class="atk-group atk-importer atk-form-wrap">
|
||||
<div class="atk-title">导入评论数据</div>
|
||||
<input type="file" name="importer_dataFile" accept="text/plain,.json" />
|
||||
<div class="atk-label">数据类型:</div>
|
||||
<div class="atk-label">数据类型</div>
|
||||
<select name="importer_dataType">
|
||||
<option value="artrans">Artrans (数据行囊)</option>
|
||||
<option value="artalk_v1">Artalk v1 (PHP 旧版)</option>
|
||||
<option value="typecho">Typecho</option>
|
||||
<option value="wordpress">WordPress</option>
|
||||
<option value="disqus">Disqus</option>
|
||||
<option value="commento">Commento</option>
|
||||
<option value="valine">Valine</option>
|
||||
<option value="twikoo">Twikoo</option>
|
||||
</select>
|
||||
<div class="atk-label">目标站点名:</div>
|
||||
<input type="file" name="importer_dataFile" accept="text/plain,.json" />
|
||||
<div class="atk-label">目标站点名</div>
|
||||
<input type="text" name="importer_siteName" />
|
||||
<div class="atk-label">目标站点 URL</div>
|
||||
<input type="text" name="importer_siteUrl" />
|
||||
<div class="atk-label">启动参数</div>
|
||||
<textarea name="importer_payload"></textarea>
|
||||
<span class="atk-desc">启动参数请查阅:“<a href="https://artalk.js.org/guide/transfer.html" target="_blank">文档 · 数据搬家</a>”</span>
|
||||
<button class="atk-btn" name="importer_submit">导入</button>
|
||||
</div>
|
||||
</div>`)
|
||||
el.append(settingEl)
|
||||
|
||||
const formWrapEl = settingEl.querySelector<HTMLElement>('.atk-form-wrap')!
|
||||
const logWrapEl = settingEl.querySelector<HTMLElement>('.atk-log-wrap')!
|
||||
const logEl = logWrapEl.querySelector<HTMLElement>('.atk-log')!
|
||||
const logBackBtnEl = logWrapEl?.querySelector<HTMLElement>('.atk-log-back-btn')!
|
||||
logBackBtnEl.onclick = () => {
|
||||
logWrapEl.style.display = 'none'
|
||||
formWrapEl.style.display = ''
|
||||
}
|
||||
|
||||
const impDataTypeEl = settingEl.querySelector<HTMLSelectElement>('[name="importer_dataType"]')!
|
||||
const impDataFileEl = settingEl.querySelector<HTMLInputElement>('[name="importer_dataFile"]')!
|
||||
const impSiteNameEl = settingEl.querySelector<HTMLInputElement>('[name="importer_siteName"]')!
|
||||
const impDataTypeEl = settingEl.querySelector<HTMLSelectElement>('[name="importer_dataType"]')!
|
||||
const impSubmitEl = settingEl.querySelector<HTMLButtonElement>('[name="importer_submit"]')!
|
||||
const impSubmitTextOrg = impSubmitEl.innerText
|
||||
impSubmitEl.onclick = () => {
|
||||
const siteName = impSiteNameEl.value.trim()
|
||||
const dataType = impDataTypeEl.value.trim()
|
||||
const impSiteUrlEl = settingEl.querySelector<HTMLInputElement>('[name="importer_siteUrl"]')!
|
||||
const impPayloadEl = settingEl.querySelector<HTMLInputElement>('[name="importer_payload"]')!
|
||||
|
||||
if (!impDataFileEl.files || impDataFileEl.files.length === 0) {
|
||||
window.alert('请打开文件')
|
||||
return
|
||||
impDataTypeEl.onchange = () => {
|
||||
if (['typecho'].includes(impDataTypeEl.value)) {
|
||||
impDataFileEl.style.display = 'none'
|
||||
} else {
|
||||
impDataFileEl.style.display = ''
|
||||
}
|
||||
}
|
||||
|
||||
const impSubmitEl = settingEl.querySelector<HTMLButtonElement>('[name="importer_submit"]')!
|
||||
impSubmitEl.onclick = () => {
|
||||
const dataType = impDataTypeEl.value.trim()
|
||||
const siteName = impSiteNameEl.value.trim()
|
||||
const siteUrl = impSiteUrlEl.value.trim()
|
||||
const payload = impPayloadEl.value.trim()
|
||||
|
||||
if (dataType === '') {
|
||||
window.alert('请选择数据类型')
|
||||
return
|
||||
}
|
||||
|
||||
const createImportSession = (data?: string) => {
|
||||
logWrapEl.style.display = ''
|
||||
formWrapEl.style.display = 'none'
|
||||
|
||||
// 创建 iframe
|
||||
const frameName = `f_${+new Date()}`
|
||||
const frame = document.createElement('iframe')
|
||||
frame.className = 'atk-iframe'
|
||||
frame.name = frameName
|
||||
logEl.innerHTML = ''
|
||||
logEl.append(frame)
|
||||
|
||||
const formEl = document.createElement('form')
|
||||
formEl.style.display = 'none'
|
||||
formEl.setAttribute('method', 'post')
|
||||
formEl.setAttribute('action', `${this.ctx.conf.server}/admin/artransfer`)
|
||||
formEl.setAttribute("target", frameName)
|
||||
|
||||
let pJSON: any = {}
|
||||
if (payload) {
|
||||
// JSON 格式检验
|
||||
try {
|
||||
pJSON = JSON.parse(payload)
|
||||
} catch (err) {
|
||||
window.alert(`Payload JSON 格式有误:${String(err)}`)
|
||||
}
|
||||
|
||||
if (pJSON !instanceof Object) {
|
||||
window.alert(`Payload 需为 JSON 对象`)
|
||||
}
|
||||
}
|
||||
|
||||
if (siteName) pJSON.t_name = siteName
|
||||
if (siteUrl) pJSON.t_url = siteUrl
|
||||
if (data) pJSON.json_data = data
|
||||
|
||||
const formParams: {[k: string]: string} = {
|
||||
type: dataType,
|
||||
payload: JSON.stringify(pJSON),
|
||||
token: this.ctx.user.data.token || '',
|
||||
}
|
||||
|
||||
Object.entries(formParams).forEach(([key, val]) => {
|
||||
const hiddenField = document.createElement('input')
|
||||
hiddenField.setAttribute('type', 'hidden')
|
||||
hiddenField.setAttribute('name', key)
|
||||
hiddenField.value = val
|
||||
formEl.appendChild(hiddenField)
|
||||
})
|
||||
|
||||
logWrapEl.append(formEl)
|
||||
formEl.submit()
|
||||
formEl.remove()
|
||||
}
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
const data = String(reader.result)
|
||||
if (!data) return
|
||||
|
||||
impSubmitEl.innerText = '请稍后...'
|
||||
new Api(this.ctx).importer(data, dataType, siteName)
|
||||
.then(() => {
|
||||
window.alert(`导入成功`)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
window.alert(`导入失败:${err.msg || '未知错误'}`)
|
||||
})
|
||||
.finally(() => {
|
||||
impSubmitEl.innerText = impSubmitTextOrg
|
||||
})
|
||||
createImportSession(data)
|
||||
}
|
||||
reader.readAsText(impDataFileEl.files[0]);
|
||||
if (impDataFileEl.files?.length) {
|
||||
reader.readAsText(impDataFileEl.files[0])
|
||||
return
|
||||
}
|
||||
|
||||
createImportSession()
|
||||
}
|
||||
|
||||
this.el.innerHTML = ''
|
||||
|
||||
+1
-14
@@ -283,19 +283,6 @@ export default class Api {
|
||||
body: getFormData(params),
|
||||
}).then((json) => (json.success as boolean))
|
||||
}
|
||||
|
||||
public importer(data: string, type: string, siteName: string) {
|
||||
const params: any = {
|
||||
data,
|
||||
type,
|
||||
site_name: siteName || '',
|
||||
}
|
||||
|
||||
return CommonFetch(this.ctx, `${this.serverURL}/admin/importer`, {
|
||||
method: 'POST',
|
||||
body: getFormData(params),
|
||||
}).then((json) => (json.success as boolean))
|
||||
}
|
||||
}
|
||||
|
||||
/** 公共请求函数 */
|
||||
@@ -309,7 +296,7 @@ function CommonFetch(ctx: Context, input: RequestInfo, init: RequestInit): Promi
|
||||
|
||||
// 15s timeout
|
||||
return timeoutPromise(15000, fetch(input, init)).then(async (resp) => {
|
||||
if (!resp.ok) {
|
||||
if (!resp.ok && resp.status !== 401) {
|
||||
throw new Error(`请求响应 ${resp.status}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
.atk-comment-children {
|
||||
.atk-comment-wrap {
|
||||
margin-top: 10px;
|
||||
border-left: 1px dashed hsla(0,0%,93%,.5);
|
||||
border-left: 1px dashed var(--at-color-border);
|
||||
|
||||
.comment-small-size()
|
||||
}
|
||||
|
||||
+44
-1
@@ -124,6 +124,7 @@
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-radius: 50px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:hover {
|
||||
color: #4285f4;
|
||||
@@ -161,14 +162,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.atk-form-wrap, .atk-form-inline-wrap {
|
||||
padding: 10px 10px;
|
||||
|
||||
input, select {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
input {
|
||||
input, textarea {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
@@ -176,6 +183,7 @@
|
||||
border: none;
|
||||
border-bottom: 1px solid @color-border;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
border-bottom-color: @color-main;
|
||||
@@ -185,9 +193,15 @@
|
||||
width: 200px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border: 0;
|
||||
margin: 17px 0;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
select {
|
||||
display: block;
|
||||
outline: none;
|
||||
@@ -195,6 +209,7 @@
|
||||
border-bottom: 1px solid @color-border;
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
@@ -212,6 +227,34 @@
|
||||
padding: 0 20px;
|
||||
border-radius: 2px;
|
||||
margin: 0 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.atk-desc {
|
||||
font-size: 14px;
|
||||
margin-top: 12px;
|
||||
margin-left: 15px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.atk-admin-setting {
|
||||
.atk-log-back-btn {
|
||||
display: inline-block;
|
||||
padding: 5px 30px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
}
|
||||
|
||||
.atk-iframe {
|
||||
width: 100%;
|
||||
height: calc(100vh - 150px);
|
||||
border: 0;
|
||||
background: #f4f4f4;
|
||||
border: 5px solid #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export default defineConfig({
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/main.ts'),
|
||||
name: 'Artalk',
|
||||
fileName: (format) => `Artalk.${format}.js`,
|
||||
fileName: (format) => ((format == "umd") ? 'Artalk.js' : `Artalk.${format}.js`),
|
||||
formats: ["es", "umd", "iife"]
|
||||
},
|
||||
rollupOptions: {
|
||||
|
||||
Reference in new issue
Block a user