注册 Serv00
注册没什么好说的, gmail+干净一点的ip基本上都可以成功, 这里不多做展开.
收不到邮件信息的估计是邮箱问题. 比如:https://mail.proton.me
准备工作
进入 Additional services 选项卡中找到 Run your own applications 项目,
如下图所示, 必须要设置成如图所示的 Enabled.
如果不开启这一项, 自己的用户目录下的所有文件都无法添加可执行权限.
部署记录
Alist
创建端口
每个账户只能创建3个端口.
其中一个用来反代本地搭建 Alist 的端口, 这里我用 26666 端口.
创建网站
Serv00 账号创建好之后默认就有一个网站, 一般是
USERNAME.serv00.net
, 可以随意删除.
下图我写的是自己的域名.
其中 26666 这个端口是 Alist 服务的端口.
网站添加 SSL 证书
站点创建完成后, 点击右边的 SSL 👉 WWW websites 👉 Manage.
点击 Add certificate, 确认好要创建证书的域名, 最后点击 Add 完成创建证书.
安装 Alist
Tip
Alist 官方在 24年8月17日, 增加了 beta 版本, 支持 FreeBSD 系统下能够运行的 Alist 可执行文件🎉
Serv00 本身提供的网站托管在~/domains
路径下, 所以我建议把 Alist 也部署到这个路径下的子目录.
一键创建目录并下载 Alist, 增加执行权限, 复制到终端粘贴使用.
mkdir -p ~/domains/alist && cd ~/domains/alist && curl -L -o alist.tar.gz https://github.com/AlistGo/alist/releases/download/beta/alist-freebsd-amd64.tar.gz && tar -xzf alist.tar.gz && chomd +x alist
第一次启动 Alist 生成配置文件
文件下载好后需要先启动一次 Alist, 让它生成配置文件, 此次一定会报错, 请不用在意~
./alist server
创建 Alist 所需数据库
回到 Panel 面板, 找到 MySQL 选项卡, 使用 Add database 功能新建一个数据库:
Database name 和 Username 字段为了方便好记就写 Alist 就行了.
密码要求含有大写字母, 小写字母和数字三种字符, 且长度必须超过6个字符.
修改配置文件
进入 Panel 面板, 找到 File manager 选项卡, 会如下图的进入文件管理器.
定位这个config.json
文件, 双击编辑它:
我主要修改了CDN
database
scheme
三个部分,
CDN |
可以在 Alist 的官方文档找到, 请选择你本地网络连接速度最快的一个. |
|
database | type | 因为我们创建的数据库类型是 mysql, 就写mysql . |
host | 例如我的 serv00 是12, 就写mysql12.serv00.com , 自己看着写. |
|
port | mysql 端口, 默认3306 . |
|
user | 填写创建的数据库用户名 | |
passdowd | 填写创建的数据密码 | |
name | 填写创建的数据表名称 | |
scheme | address | 0.0.0.0 |
http_port | 填写创建的端口 |
改完之后, 点击 save 保存.
再次启动 Alist
回到 SSH 窗口中进行操作.
./alist server
运行正常, 显示的管理员账号的密码一定要记住. 接着使用 Ctrl+c 停止运行.
自定义域名绑定
我这边使用 us.kg 的免费域名进行访问 Alist.
因为 serv00 的域名基本上都会被墙, 没办法只能用Cloudflare减速器跨墙了, CDN 回源加速不会弄.
我们进入 https://dash.cloudflare.com
点击添加域, 再输入自己的域名, 选择最底下的 free 计划一路创建.
然后复制 Cloudflare 给的 dns 名称服务器.
然后转到你的域名提供服务商, 添加 dns 记录.
如果没问题就可以通过自定义域名访问了.
Alist 保活
因为 Serv00 会不定时杀进程😅, 所以诞生此方案.
我在 Alist 目录下创建了runAlist.sh
脚本, 内容如下:
screen -ls | awk 'NR>=2&&NR<=20{print $1}' | awk '{print "screen -S "$1" -X quit"}' | sh
echo "Attempting to start screen session 'alist'"
screen -dmS alist bash -c 'cd ~/domains/alist && ./alist server'
#screen -ls
echo "$(date '+%Y-%m-%d %H:%M:%S')" > ~/domains/alist/logfile.txt
转到 panel 面板, 创建 Cron Jobs 定时任务.
我们需要创建一个每小时执行的任务进行保活, 如下图:
计划任务执行的是我的runAlist.sh
脚本.
不出意外的话, 隔一段时间进入 Alist 网页需要重新登陆账号, 因为定时脚本会先杀原来的 Alist 进程再重启.
Serv00+CT8 保活(可TG通知)
引用自 Linux.do
先决条件
- 假设你已经拥有了一个 telegram bot.
首先上代码
Worker 代码:
JavaScript Code
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
addEventListener('scheduled', event => {
event.waitUntil(handleScheduled(event.scheduledTime))
})
async function handleRequest(request) {
return new Response('Worker is running')
}
async function handleScheduled(scheduledTime) {
const accounts = JSON.parse(ACCOUNTS_JSON)
const results = await loginAccounts(accounts)
await sendSummary(results)
}
async function loginAccounts(accounts) {
const results = []
for (const account of accounts) {
const result = await loginAccount(account)
results.push({ ...account, ...result })
await delay(Math.floor(Math.random() * 8000) + 1000)
}
return results
}
function generateRandomUserAgent() {
const browsers = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera'];
const browser = browsers[Math.floor(Math.random() * browsers.length)];
const version = Math.floor(Math.random() * 100) + 1;
const os = ['Windows NT 10.0', 'Macintosh', 'X11'];
const selectedOS = os[Math.floor(Math.random() * os.length)];
const osVersion = selectedOS === 'X11' ? 'Linux x86_64' : selectedOS === 'Macintosh' ? 'Intel Mac OS X 10_15_7' : 'Win64; x64';
return `Mozilla/5.0 (${selectedOS}; ${osVersion}) AppleWebKit/537.36 (KHTML, like Gecko) ${browser}/${version}.0.0.0 Safari/537.36`;
}
async function loginAccount(account) {
const { username, password, panelnum, type } = account
let url = type === 'ct8'
? 'https://panel.ct8.pl/login/?next=/'
: `https://panel${panelnum}.serv00.com/login/?next=/`
const userAgent = generateRandomUserAgent();
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'User-Agent': userAgent,
},
})
const pageContent = await response.text()
const csrfMatch = pageContent.match(/name="csrfmiddlewaretoken" value="([^"]*)"/)
const csrfToken = csrfMatch ? csrfMatch[1] : null
if (!csrfToken) {
throw new Error('CSRF token not found')
}
const initialCookies = response.headers.get('set-cookie') || ''
const formData = new URLSearchParams({
'username': username,
'password': password,
'csrfmiddlewaretoken': csrfToken,
'next': '/'
})
const loginResponse = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': url,
'User-Agent': userAgent,
'Cookie': initialCookies,
},
body: formData.toString(),
redirect: 'manual'
})
console.log(`Login response status: ${loginResponse.status}`)
console.log(`Login response headers: ${JSON.stringify(Object.fromEntries(loginResponse.headers))}`)
const loginResponseBody = await loginResponse.text()
console.log(`Login response body: ${loginResponseBody.substring(0, 200)}...`)
if (loginResponse.status === 302 && loginResponse.headers.get('location') === '/') {
const loginCookies = loginResponse.headers.get('set-cookie') || ''
const allCookies = combineCookies(initialCookies, loginCookies)
const dashboardResponse = await fetch(url.replace('/login/', '/'), {
headers: {
'Cookie': allCookies,
'User-Agent': userAgent,
}
})
const dashboardContent = await dashboardResponse.text()
console.log(`Dashboard content: ${dashboardContent.substring(0, 200)}...`)
if (dashboardContent.includes('href="/logout/"') || dashboardContent.includes('href="/wyloguj/"')) {
const nowUtc = formatToISO(new Date())
const nowBeijing = formatToISO(new Date(Date.now() + 8 * 60 * 60 * 1000))
const message = `账号 ${username} (${type}) 于北京时间 ${nowBeijing}(UTC时间 ${nowUtc})登录成功!`
console.log(message)
await sendTelegramMessage(message)
return { success: true, message }
} else {
const message = `账号 ${username} (${type}) 登录后未找到登出链接,可能登录失败。`
console.error(message)
await sendTelegramMessage(message)
return { success: false, message }
}
} else if (loginResponseBody.includes('Nieprawidłowy login lub hasło')) {
const message = `账号 ${username} (${type}) 登录失败: 用户名或密码错误。`
console.error(message)
await sendTelegramMessage(message)
return { success: false, message }
} else {
const message = `账号 ${username} (${type}) 登录失败,未知原因。请检查账号和密码是否正确。`
console.error(message)
await sendTelegramMessage(message)
return { success: false, message }
}
} catch (error) {
const message = `账号 ${username} (${type}) 登录时出现错误: ${error.message}`
console.error(message)
await sendTelegramMessage(message)
return { success: false, message }
}
}
function combineCookies(cookies1, cookies2) {
const cookieMap = new Map()
const parseCookies = (cookieString) => {
cookieString.split(',').forEach(cookie => {
const [fullCookie] = cookie.trim().split(';')
const [name, value] = fullCookie.split('=')
if (name && value) {
cookieMap.set(name.trim(), value.trim())
}
})
}
parseCookies(cookies1)
parseCookies(cookies2)
return Array.from(cookieMap.entries()).map(([name, value]) => `${name}=${value}`).join('; ')
}
async function sendSummary(results) {
const successfulLogins = results.filter(r => r.success)
const failedLogins = results.filter(r => !r.success)
let summaryMessage = '登录结果统计: \n'
summaryMessage += `成功登录的账号: ${successfulLogins.length}\n`
summaryMessage += `登录失败的账号: ${failedLogins.length}\n`
if (failedLogins.length > 0) {
summaryMessage += '\n登录失败的账号列表: \n'
failedLogins.forEach(({ username, type, message }) => {
summaryMessage += `- ${username} (${type}): ${message}\n`
})
}
console.log(summaryMessage)
await sendTelegramMessage(summaryMessage)
}
async function sendTelegramMessage(message) {
const telegramConfig = JSON.parse(TELEGRAM_JSON)
const { telegramBotToken, telegramBotUserId } = telegramConfig
const url = `https://api.telegram.org/bot${telegramBotToken}/sendMessage`
try {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: telegramBotUserId,
text: message
})
})
} catch (error) {
console.error('Error sending Telegram message:', error)
}
}
function formatToISO(date) {
return date.toISOString().replace('T', ' ').replace('Z', '').replace(/\.\d{3}Z/, '')
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
变量:
ACCOUNTS_JSON
Json
[
{ "username": "serv00user1", "password": "serv00password1", "panelnum": "0", "type": "serv00" },
{ "username": "serv00user2", "password": "serv00password2", "panelnum": "4", "type": "serv00" },
{ "username": "serv00user3", "password": "serv00password3", "panelnum": "7", "type": "serv00" },
{ "username": "ct8user1", "password": "ct8password1", "type": "ct8" },
{ "username": "ct8user2", "password": "ct8password2", "type": "ct8" }
]
TELEGRAM_JSON
Json
{
"telegramBotToken": "YOUR_BOT_TOKEN",
"telegramBotUserId": "YOUR_USER_ID"
}
创建 Workers
进入 Cloudflare 面板, 创建 Workers
名字随意, 建议写Serv00Keep
方便好记, 然后右下角点部署.
部署完成后点击编辑代码.
粘贴 JavaScript 代码之后再点击部署.
添加机密
返回到Serv00Keep
的设置, 找到变量和机密
按照Json
格式编辑好自己的 serv00 账号和密码, 创建ACCOUNTS_JSON
变量.
按照Json
格式编辑好自己的 telegram bot token, 创建TELEGRAM_JSON
变量.
添加触发事件
手动部署
机密和触发事件填写完成之后, 我们手动部署一次.
手动执行验证效果
如下图, 进入编辑代码 → 设定时间 → 触发计划的事件, 即可手动触发.
同时可以看到正常运行没问题, 接下来就是定时执行不用再管它了.