Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
LEAN_ID=<Your LeanCloud ID>
LEAN_KEY=<Your LeanCloud Key>
LEAN_MASTER_KEY=<Your LeanCloud Master Key>
LEAN_SERVER=<Your LeanCloud Server>
POSTGRES_DATABASE=
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_HOST=
PORSTGRES_PORT=
POSTGRES_PREFIX=
POSTGRES_SSL=
6 changes: 6 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ jobs:
- name: Check out repository
uses: actions/checkout@v4

- name: Pack webhook plugin
run: |
cd waline-plugin-webhook
npm pack
mv waline-plugin-webhook-*.tgz ../

- name: Set Docker image tag
id: set-tag
run: |
Expand Down
118 changes: 118 additions & 0 deletions .github/workflows/sync-waline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: Sync Waline with Official Repository

on:
schedule:
- cron: '0 0 * * 0' # 每周日运行
workflow_dispatch: # 允许手动触发

jobs:
sync:
runs-on: windows-latest
timeout-minutes: 30

steps:
- name: Checkout local repository
uses: actions/checkout@v4
with:
path: local
fetch-depth: 0

- name: Checkout official Waline repository
uses: actions/checkout@v4
with:
repository: walinejs/waline
path: official
ref: main

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'

- name: Compare version numbers
id: compare-versions
run: |
$localVersion = Get-Content -Path "local/waline/package.json" | ConvertFrom-Json | Select-Object -ExpandProperty version
$officialVersion = Get-Content -Path "official/packages/server/package.json" | ConvertFrom-Json | Select-Object -ExpandProperty version

Write-Host "Local version: $localVersion"
Write-Host "Official version: $officialVersion"

if ($localVersion -ne $officialVersion) {
Write-Host "Versions differ, proceeding with sync"
echo "needs-sync=true" >> $env:GITHUB_OUTPUT
echo "local-version=$localVersion" >> $env:GITHUB_OUTPUT
echo "official-version=$officialVersion" >> $env:GITHUB_OUTPUT
} else {
Write-Host "Versions match, skipping sync"
echo "needs-sync=false" >> $env:GITHUB_OUTPUT
}
shell: pwsh

- name: Sync files
if: steps.compare-versions.outputs.needs-sync == 'true'
run: |
# Create sync directory
New-Item -ItemType Directory -Path "sync" -Force

# Copy official files to sync directory
Copy-Item -Path "official/packages/server/*" -Destination "sync" -Recurse -Force

# Remove files that should not be synced
Remove-Item -Path "sync/Dockerfile" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "sync/config.js" -Force -ErrorAction SilentlyContinue

# Process dashboard.js - preserve title and icon
if (Test-Path "sync/src/middleware/dashboard.js") {
$dashboardContent = Get-Content -Path "sync/src/middleware/dashboard.js" -Raw
$localDashboardContent = Get-Content -Path "local/waline/src/middleware/dashboard.js" -Raw

# Extract title and icon lines from local file
$titleLine = $localDashboardContent -match '<title>.*<\/title>' | ForEach-Object { $Matches[0] }
$iconLine = $localDashboardContent -match '<link rel="icon" href=".*">' | ForEach-Object { $Matches[0] }

# Replace title and icon in sync file
if ($titleLine -and $iconLine) {
$dashboardContent = $dashboardContent -replace '<title>.*<\/title>', $titleLine
$dashboardContent = $dashboardContent -replace '<link rel="icon" href=".*">', $iconLine
Set-Content -Path "sync/src/middleware/dashboard.js" -Value $dashboardContent
}
}

# Process index.js - preserve title and icon
if (Test-Path "sync/src/controller/index.js") {
$indexContent = Get-Content -Path "sync/src/controller/index.js" -Raw
$localIndexContent = Get-Content -Path "local/waline/src/controller/index.js" -Raw

# Extract title and icon lines from local file
$titleLine = $localIndexContent -match '<title>.*<\/title>' | ForEach-Object { $Matches[0] }
$iconLine = $localIndexContent -match '<link rel="icon" href=".*">' | ForEach-Object { $Matches[0] }

# Replace title and icon in sync file
if ($titleLine -and $iconLine) {
$indexContent = $indexContent -replace '<title>.*<\/title>', $titleLine
$indexContent = $indexContent -replace '<link rel="icon" href=".*">', $iconLine
Set-Content -Path "sync/src/controller/index.js" -Value $indexContent
}
}

# Copy synced files to local directory
Copy-Item -Path "sync/*" -Destination "local/waline" -Recurse -Force

# Update version in local package.json
$localPackageJson = Get-Content -Path "local/waline/package.json" | ConvertFrom-Json
$officialPackageJson = Get-Content -Path "official/packages/server/package.json" | ConvertFrom-Json
$localPackageJson.version = $officialPackageJson.version
$localPackageJson | ConvertTo-Json -Depth 100 | Set-Content -Path "local/waline/package.json"
shell: pwsh

- name: Commit and push changes
if: steps.compare-versions.outputs.needs-sync == 'true'
run: |
cd local
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add .
git commit -m "Sync with official Waline repository (v${{ steps.compare-versions.outputs.official-version }})"
git push
shell: bash
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
.vercel
node_modules
2 changes: 1 addition & 1 deletion index.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const Application = require('waline');
const Application = require('@waline/vercel');

module.exports = Application({
plugins: [],
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "0.0.1",
"private": true,
"dependencies": {
"waline": "file:./waline"
"@waline/vercel": "file:./waline",
"@waline-plugins/tencent-tms": "latest",
"waline-plugin-llm-reviewer": "file:./waline-plugin-llm-reviewer",
"waline-plugin-webhook": "file:./waline-plugin-webhook"
}
}
6 changes: 5 additions & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
"silent": true
},
"builds": [
{
"src": "robots.txt",
"use": "@vercel/static"
},
{
"src": "index.cjs",
"use": "@vercel/node"
}
],
"rewrites": [
{
"source": "/(.*)",
"source": "/((?!robots\\.txt$).*)",
"destination": "index.cjs"
}
]
Expand Down
107 changes: 107 additions & 0 deletions waline-plugin-webhook/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const nunjucks = require('nunjucks');

module.exports = function ({ webhookUrl, webhookTemplate, webhookHeaders }) {
const { WEBHOOK_URL, WEBHOOK_TEMPLATE, WEBHOOK_HEADERS, SITE_NAME, SITE_URL } = process.env;

const url = webhookUrl || WEBHOOK_URL;
const template = webhookTemplate || WEBHOOK_TEMPLATE;
const headers = webhookHeaders || WEBHOOK_HEADERS;

if (!url) {
return {};
}

const parsedHeaders = {};
if (headers) {
headers.split('\n').forEach(line => {
const [key, ...valueParts] = line.split(':');
if (key && valueParts.length > 0) {
parsedHeaders[key.trim()] = valueParts.join(':').trim();
}
});
}

// 处理评论内容:解码 HTML 实体并移除 HTML 标签
const decodeHTML = (str) => {
if (typeof str !== 'string') return str;
return str
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/(<([^>]+)>)/gi, ''); // 移除所有 HTML 标签
};

const sendWebhook = async (data, parent = null) => {
// 准备模板数据
const templateData = {
self: {
...data,
comment: decodeHTML(data.comment),
objectId: data.objectId || data.id
},
parent: parent ? {
...parent,
comment: decodeHTML(parent.comment),
objectId: parent.objectId || parent.id
} : null,
site: {
name: SITE_NAME,
url: SITE_URL,
postUrl: SITE_URL + (data.url || '') + '#' + (data.objectId || data.id),
},
};

// 使用自定义模板或默认模板
const webhookTemplate = template ||
`【新评论通知】{{site.name}}
========================
💬 评论者:{{self.nick}}{% if self.mail %} ({{self.mail}}){% endif %}
📍 归属地:{% if self.addr %}{{self.addr}}{% else %}未知{% endif %}
💻 设备:{{self.os}} / {{self.browser}}
📋 状态:{% if self.status == 'approved' %}审核通过{% elif self.status == 'waiting' %}等待审核{% elif self.status == 'spam' %}垃圾评论{% else %}{{self.status}}{% endif %}

{% if self.status == 'approved' %}{{self.comment}}{% elif self.status == 'waiting' %}云审查疑似失效,评论等待人工审核,请前往站点审核{% elif self.status == 'spam' %}垃圾评论,请人工审核{% else %}未知评论状态:{{self.status}},请人工审核{% endif %}
{% if parent %}
========================
此评论回复了:{{parent.nick}}{% if parent.mail %} ({{parent.mail}}){% endif %}
{% if parent.status == 'approved' %}{{parent.comment}}{% elif parent.status == 'waiting' %}云审查疑似失效,评论等待人工审核,请前往站点审核{% elif parent.status == 'spam' %}垃圾评论,请人工审核{% else %}未知评论状态:{{parent.status}},请人工审核{% endif %}
{% endif %}`;

// 渲染模板
const renderedBody = nunjucks.renderString(webhookTemplate, templateData);

const body = {
Title: 'Waline Notify',
Body: renderedBody
};

try {
const resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...parsedHeaders
},
body: JSON.stringify(body),
});

return resp.ok;
} catch (error) {
console.error('Waline webhook error:', error);
return false;
}
};

return {
hooks: {
async postSave(comment, pComment) {
await sendWebhook(comment, pComment);
},
async postReply(comment, pComment) {
await sendWebhook(comment, pComment);
}
}
};
}
12 changes: 12 additions & 0 deletions waline-plugin-webhook/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "waline-plugin-webhook",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
40 changes: 40 additions & 0 deletions waline/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage/

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules/

# IDE config
.idea

# output
output/
output.tar.gz

runtime/
app/

config.development.js
adapter.development.js

.vercel
.env
17 changes: 10 additions & 7 deletions waline/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# https://github.com/nodejs/LTS
FROM node:lts AS build
WORKDIR /app
ENV NODE_ENV=production
ENV NODE_ENV="production"
COPY waline-plugin-webhook-*.tgz /tmp/
RUN set -eux; \
# npm config set registry https://registry.npmmirror.com; \
npm install --production --silent @waline/vercel \
npm install --production --silent @waline-plugins/tencent-tms \
npm install --production --silent waline-plugin-llm-reviewer
# npm config set registry https://registry.npm.taobao.org; \
npm install --production --silent @waline/vercel; \
npm install --production --silent @waline-plugins/tencent-tms; \
npm install --production --silent waline-plugin-llm-reviewer; \
npm install --production --silent /tmp/waline-plugin-webhook-*.tgz; \
rm -rf /tmp/waline-plugin-webhook-*.tgz

COPY waline/config.js ./node_modules/@waline/vercel/config.js
COPY waline/src/service/notify.js ./node_modules/@waline/vercel/src/service/notify.js
Expand All @@ -17,8 +20,8 @@ COPY waline-plugin-llm-reviewer/index.js ./node_modules/waline-plugin-llm-review

FROM node:lts-slim
WORKDIR /app
ENV TZ=Asia/Shanghai
ENV NODE_ENV=production
ENV TZ="Asia/Shanghai"
ENV NODE_ENV="production"
COPY --from=build /app .
EXPOSE 8360
CMD ["node", "node_modules/@waline/vercel/vanilla.js"]
6 changes: 3 additions & 3 deletions waline/Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# https://github.com/nodejs/LTS
FROM node:lts AS build
WORKDIR /app
ENV NODE_ENV production
ENV NODE_ENV="production"
RUN set -eux; \
# npm config set registry https://registry.npm.taobao.org; \
npm install --production --silent @waline/vercel

FROM node:lts-alpine
WORKDIR /app
ENV TZ Asia/Shanghai
ENV NODE_ENV production
ENV TZ="Asia/Shanghai"
ENV NODE_ENV="production"
RUN set -eux; \
# sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories; \
apk add --no-cache bash; \
Expand Down
Loading