generated from template/slidev-template
update
This commit is contained in:
11
agent/corn.ts
Normal file
11
agent/corn.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import cron from 'node-cron';
|
||||
import './index.ts'
|
||||
import { main } from './task.ts';
|
||||
|
||||
// 每2个小时执行一次更新任务
|
||||
cron.schedule('0 */2 * * *', async () => {
|
||||
main()
|
||||
});
|
||||
|
||||
// first run
|
||||
main()
|
||||
8
agent/ddns/get-ip.ts
Normal file
8
agent/ddns/get-ip.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
const baseURLv4 = 'https://4.ipw.cn/';
|
||||
const baseURLv6 = 'https://6.ipw.cn/';
|
||||
export const getPublicIp = async (type: 'v4' | 'v6'): Promise<string> => {
|
||||
const url = type === 'v4' ? baseURLv4 : baseURLv6;
|
||||
const response = await fetch(url);
|
||||
const ip = (await response.text()).trim();
|
||||
return ip;
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
import {app} from './app.ts';
|
||||
|
||||
import './ip'
|
||||
|
||||
import './task.ts';
|
||||
import './routes/cloudflare.ts';
|
||||
// app.listen(8080);
|
||||
|
||||
app.call({
|
||||
path: 'ip',
|
||||
key: 'v6'
|
||||
}).then(res => {
|
||||
console.log('IPv4 Address:', res);
|
||||
})
|
||||
|
||||
export { app };
|
||||
@@ -1,10 +1,8 @@
|
||||
const baseURLv4 = 'https://4.ipw.cn/';
|
||||
const baseURLv6 = 'https://6.ipw.cn/';
|
||||
|
||||
|
||||
import { app } from './app.ts';
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'ip',
|
||||
key: 'v4',
|
||||
@@ -12,7 +10,7 @@ app.route({
|
||||
}).define(async (ctx) => {
|
||||
const response = await fetch(baseURLv4);
|
||||
const ip = (await response.text()).trim();
|
||||
if(!isIpv4(ip)) {
|
||||
if (!isIpv4(ip)) {
|
||||
ctx.throw?.('获取地址失败');
|
||||
}
|
||||
ctx.body = { ip };
|
||||
@@ -25,7 +23,7 @@ app.route({
|
||||
}).define(async (ctx) => {
|
||||
const response = await fetch(baseURLv6);
|
||||
const ip = (await response.text()).trim();
|
||||
if(!isIpv6(ip)) {
|
||||
if (!isIpv6(ip)) {
|
||||
ctx.throw?.('获取地址失败');
|
||||
}
|
||||
ctx.body = { ip };
|
||||
@@ -38,4 +36,5 @@ export const isIpv6 = (ip: string): boolean => {
|
||||
|
||||
export const isIpv4 = (ip: string): boolean => {
|
||||
return ip.split('.').length === 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
agent/routes/cloudflare.ts
Normal file
26
agent/routes/cloudflare.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {app} from '../app.ts';
|
||||
import { CloudflareDDNS } from '../ddns/cloudflare/index.ts';
|
||||
|
||||
app.route({
|
||||
path: 'cf',
|
||||
key: 'update',
|
||||
description: '更新Cloudflare DNS记录, 需要提供zone_id, record_id, domain, new_ip, api_token, type参数, type参数可选,默认为A记录, A 或AAAA',
|
||||
}).define(async (ctx) => {
|
||||
const { zone_id, record_id, domain, new_ip, api_token, type = 'A' } = ctx.query || {};
|
||||
|
||||
if(!zone_id || !record_id || !domain || !new_ip || !api_token) {
|
||||
ctx.throw?.('缺少必要参数');
|
||||
}
|
||||
|
||||
const cf = new CloudflareDDNS();
|
||||
const result = await cf.updateRecord({
|
||||
zone_id,
|
||||
record_id,
|
||||
domain,
|
||||
new_ip,
|
||||
api_token,
|
||||
type,
|
||||
});
|
||||
|
||||
ctx.body = { result };
|
||||
}).addTo(app);
|
||||
92
agent/task.ts
Normal file
92
agent/task.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { app } from './app.ts';
|
||||
import { createStorage } from "unstorage";
|
||||
import fsDriver from "unstorage/drivers/fs";
|
||||
|
||||
const storage = createStorage({
|
||||
driver: fsDriver({
|
||||
base: process.cwd() + '/storage/ddns-agent'
|
||||
}),
|
||||
});
|
||||
|
||||
type CloudflareConfig = {
|
||||
// Cloudflare 访问地址
|
||||
website: string;
|
||||
domain: string;
|
||||
zone_id: string;
|
||||
// 更新IPv4记录的ID
|
||||
record_id4: string;
|
||||
// 更新IPv6记录的ID
|
||||
record_id6: string;
|
||||
api_token: string;
|
||||
ipv6: string;
|
||||
ipv4: string;
|
||||
flag: number; // 0: 不更新, 1: 仅IPv4, 2: 仅IPv6, 3: IPv4和IPv6
|
||||
time: string; // 上次更新时间戳
|
||||
}
|
||||
app.route({
|
||||
path: 'ip',
|
||||
key: 'task',
|
||||
description: `执行IP更新任务
|
||||
1. 读取配置文件cloudflare.json
|
||||
2. 根据flag决定更新IPv4和/或IPv6地址
|
||||
3. 获取当前公网IP地址
|
||||
4. 如果IP地址有变化则调用 router: cf/update 更新DNS记录
|
||||
5. 保存最新的IP地址和更新时间戳到配置文件
|
||||
`,
|
||||
}).define(async (ctx) => {
|
||||
const config = await storage.getItem<CloudflareConfig>('cloudflare.json');
|
||||
if (!config) {
|
||||
ctx.throw?.('未找到配置');
|
||||
}
|
||||
const now = Date.now();
|
||||
const date = new Date(now);
|
||||
const updateIp = async (isV4 = true) => {
|
||||
const res = await app.call({ path: 'ip', key: isV4 ? 'v4' : 'v6' });
|
||||
if (res.code !== 200) {
|
||||
ctx.throw(res.message);
|
||||
}
|
||||
const newIp = res.body.ip as string;
|
||||
const oldIp = isV4 ? config.ipv4 : config.ipv6;
|
||||
if (newIp !== oldIp) {
|
||||
// IP地址有变化,更新DNS记录
|
||||
await app.call({ path: 'cf', key: 'update' }, {
|
||||
zone_id: config.zone_id,
|
||||
record_id: isV4 ? config.record_id4 : config.record_id6,
|
||||
domain: config.domain,
|
||||
new_ip: newIp,
|
||||
api_token: config.api_token,
|
||||
type: isV4 ? 'A' : 'AAAA',
|
||||
});
|
||||
// 更新配置文件中的IP地址
|
||||
if (isV4) {
|
||||
config.ipv4 = newIp;
|
||||
} else {
|
||||
config.ipv6 = newIp;
|
||||
}
|
||||
config.time = date.toLocaleString();
|
||||
await storage.setItem('cloudflare.json', config);
|
||||
console.log(date.toLocaleString() + ` 更新 ${isV4 ? 'IPv4' : 'IPv6'} 地址为: ${newIp}`);
|
||||
} else {
|
||||
console.log(date.toLocaleString() + ` ${isV4 ? 'IPv4' : 'IPv6'} 地址未改变: ${newIp}`);
|
||||
}
|
||||
}
|
||||
if (config.flag === 1) {
|
||||
await updateIp(true);
|
||||
}
|
||||
if (config.flag === 2) {
|
||||
await updateIp(false);
|
||||
}
|
||||
if (config.flag === 3) {
|
||||
await updateIp(true);
|
||||
await updateIp(false);
|
||||
}
|
||||
ctx.body = { message: '任务完成' };
|
||||
}).addTo(app);
|
||||
|
||||
export const main = () => {
|
||||
app.call({
|
||||
path: 'ip',
|
||||
key: 'task',
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user