CTO.NEW邀请码
vmwshhdgyc7f
hzbf5jnqjmgr
2ltfdlcq58yb vmwshhdgyc7f
hzbf5jnqjmgr
2ltfdlcq58yb 该脚本实现批量删除指定日期内久未删除的分支:
./git-cleanup.sh -d 90(天数)创建保护分支列表文件:
echo -e "production\nlegacy-system" > ~/.git_cleanup_protected指定自定义配置文件:
./git-cleanup.sh -c ./project_protected_branches.cfg -d 60配置文件格式说明:
查看当前保护分支列表
# 使用示例
$ ./git-cleanup.sh -l
当前受保护分支列表:
----------------------------------------
分支名称 来源
----------------------------------------
production [配置文件] /home/user/.git_cleanup_protected
main [系统默认]
master [系统默认]
develop [系统默认]
----------------------------------------
(配置文件路径: /home/user/.git_cleanup_protected)git-cleanup.sh:
#!/bin/bash
# 默认配置
DEFAULT_PROTECTED=("master" "main" "develop") # 默认受保护分支
CONFIG_FILE="${HOME}/.git_cleanup_protected" # 默认配置文件路径
remote="origin" # 远程仓库名称
delete_local=true # 是否删除本地分支
days_threshold=30 # 默认过期天数阈值
# 加载配置文件
declare -a protected_branches=()
load_protected_branches() {
# 清空数组重新加载
protected_branches=()
# 读取配置文件
if [ -f "$CONFIG_FILE" ]; then
while IFS= read -r line; do
line=${line%%#*} # 去除行末注释
line=${line//$'\r'/} # 处理Windows换行符
line=${line// /} # 去除空格
[[ -n "$line" ]] && protected_branches+=("$line")
done < "$CONFIG_FILE"
fi
# 合并默认保护分支(去重)
for branch in "${DEFAULT_PROTECTED[@]}"; do
if [[ ! " ${protected_branches[@]} " =~ " $branch " ]]; then
protected_branches+=("$branch")
fi
done
}
# 列出保护分支
list_protected_branches() {
# 读取配置文件内容(不合并默认值)
declare -a config_branches=()
if [ -f "$CONFIG_FILE" ]; then
while IFS= read -r line; do
line=${line%%#*}
line=${line// /}
[[ -n "$line" ]] && config_branches+=("$line")
done < "$CONFIG_FILE"
fi
echo "当前受保护分支列表:"
echo "----------------------------------------"
printf "%-25s %s\n" "分支名称" "来源"
echo "----------------------------------------"
# 显示配置文件中的分支
for branch in "${config_branches[@]}"; do
printf "%-25s %s\n" "$branch" "[配置文件] $CONFIG_FILE"
done
# 显示未在配置文件中出现的默认分支
for default_branch in "${DEFAULT_PROTECTED[@]}"; do
if [[ ! " ${config_branches[@]} " =~ " $default_branch " ]]; then
printf "%-25s %s\n" "$default_branch" "[系统默认]"
fi
done
echo "----------------------------------------"
echo "(配置文件路径: $CONFIG_FILE)"
exit 0
}
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-d|--days)
days_threshold="$2"
shift; shift ;;
-c|--config)
CONFIG_FILE="$2"
shift; shift ;;
-l|--list)
list_protected_branches ;;
-h|--help)
echo "用法: $0 [选项]"
echo "选项:"
echo " -d, --days N 设置过期天数阈值(默认:30)"
echo " -c, --config FILE指定配置文件路径(默认:${CONFIG_FILE})"
echo " -l, --list 列出所有受保护分支"
echo " -h, --help 显示帮助信息"
exit 0 ;;
*)
echo "未知选项: $1"; exit 1 ;;
esac
done
# 验证天数参数
[[ "$days_threshold" =~ ^[0-9]+$ ]] || { echo "错误:天数参数必须为整数"; exit 1; }
# 加载保护分支列表
load_protected_branches
# 计算截止时间戳
cutoff=$(date -d "$days_threshold days ago" +%s)
# 获取分支信息(含创建者)
declare -a branches_info
while read ts branch; do
branch_name=${branch#$remote/}
# 跳过受保护分支
[[ " ${protected_branches[@]} " =~ " $branch_name " ]] && continue
# 获取分支创建者信息
creator=$(git log --reverse --format="%an <%ae>" "$branch" -1 2>/dev/null || echo "未知")
creator=${creator//|/-} # 处理特殊字符
# 存储分支信息:时间戳|分支名|创建者
branches_info+=("$ts|$branch_name|${creator//$'\n'/}")
done < <(git fetch -pq && git for-each-ref --sort=committerdate \
--format='%(committerdate:unix) %(refname:short)' \
"refs/remotes/$remote")
# 筛选待删除分支
branches_to_delete=()
for entry in "${branches_info[@]}"; do
IFS='|' read ts branch_name creator <<< "$entry"
[ $ts -lt $cutoff ] && branches_to_delete+=("$branch_name|$creator")
done
# 显示结果
if [ ${#branches_to_delete[@]} -eq 0 ]; then
echo "没有需要清理的分支(阈值:${days_threshold}天)"
exit 0
fi
echo "以下分支超过 ${days_threshold} 天未更新(共 ${#branches_to_delete[@]} 个):"
printf "%-30s %s\n" "分支名称" "创建者"
echo "------------------------------------------------"
for entry in "${branches_to_delete[@]}"; do
IFS='|' read branch creator <<< "$entry"
printf "%-30s %s\n" "$branch" "$creator"
done
# 用户确认
read -p "是否确认删除这些分支?[y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && { echo "操作已取消"; exit 1; }
# 执行删除
deleted_count=0
for entry in "${branches_to_delete[@]}"; do
IFS='|' read branch_name creator <<< "$entry"
# 删除远程分支
if git push -q "$remote" --delete "$branch_name"; then
echo "已删除远程分支: $branch_name (创建者: $creator)"
((deleted_count++))
else
echo "删除远程分支失败: $branch_name" >&2
continue
fi
# 删除本地分支
if [ "$delete_local" = true ]; then
git branch -D "$branch_name" >/dev/null 2>&1 && \
echo "已删除本地分支: $branch_name" || \
echo "本地分支不存在: $branch_name"
fi
done
echo "操作完成,共删除 $deleted_count 个远程分支" 脚本install-app-as-service.sh:
#!/bin/bash
# 检查参数是否正确
if [ $# -ne 1 ]; then
echo "Usage: $0 <target-directory or executable-file>"
exit 1
fi
TARGET="$1"
# 根据输入参数的类型选择操作模式
if [ -f "$TARGET" ] && [ -x "$TARGET" ]; then
# 单个可执行文件模式
echo "Single executable file detected. Installing as a service."
# 获取基本信息
executable="$TARGET"
service_name=$(basename "$executable").service
service_path="/etc/systemd/system/$service_name"
exec_path=$(realpath "$executable")
work_dir=$(dirname "$exec_path")
echo "Installing $exec_path as systemd service: $service_name"
# 生成服务文件内容(包含路径转义)
service_content="[Unit]
Description=Service for $(basename "$executable")
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=$work_dir
ExecStart=\"$exec_path\"
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target"
# 写入系统服务文件
echo "$service_content" | sudo tee "$service_path" > /dev/null
# 设置文件权限
sudo chmod 644 "$service_path"
# 重新加载systemd并启用服务
sudo systemctl daemon-reload
sudo systemctl enable "$service_name"
sudo systemctl start "$service_name"
echo "Successfully installed and started $service_name"
elif [ -d "$TARGET" ]; then
# 目录模式,与原来功能一致
echo "Directory mode: Installing all executables in directory as services."
# 查找所有可执行文件并处理
find "$TARGET" -maxdepth 1 -type f -executable | while read -r executable; do
# 获取基本信息
service_name=$(basename "$executable").service
service_path="/etc/systemd/system/$service_name"
exec_path=$(realpath "$executable")
work_dir=$(dirname "$exec_path")
echo "Installing $exec_path as systemd service: $service_name"
# 生成服务文件内容(包含路径转义)
service_content="[Unit]
Description=Service for $(basename "$executable")
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=$work_dir
ExecStart=\"$exec_path\"
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target"
# 写入系统服务文件
echo "$service_content" | sudo tee "$service_path" > /dev/null
# 设置文件权限
sudo chmod 644 "$service_path"
# 重新加载systemd并启用服务
sudo systemctl daemon-reload
sudo systemctl enable "$service_name"
sudo systemctl start "$service_name"
echo "Successfully installed and started $service_name"
done
echo "All executable services installed from $TARGET"
else
echo "Error: '$TARGET' is neither a valid directory nor an executable file."
exit 1
fi使用:
sudo install-app-as-service.sh path-to-the-app(指向单个可执行程序)
sudo install-app-as-service.sh path-to-the-dir(指向可执行程序上级目录,支持多个批量安装) // ==UserScript==
// @name SillyDev Auto Renewal
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Automatically renew servers on panel.sillydev.co.uk
// @author Your name
// @match https://panel.sillydev.co.uk/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const RENEWAL_COST = 175; // Cost per renewal
const DAYS_PER_RENEWAL = 7; // Days added per renewal
const MAX_RENEWAL_DAYS = 21; // Maximum renewal days allowed
const CHECK_INTERVAL = 30 * 60 * 1000; // Check every 30 minutes
// Helper function to get XSRF token from cookie
function getXsrfToken() {
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [name, value] = cookie.trim().split('=');
if (name === 'XSRF-TOKEN') {
return decodeURIComponent(value);
}
}
return null;
}
// Helper function to make API requests
async function makeRequest(endpoint, method = 'GET', body = null) {
const xsrfToken = getXsrfToken();
if (!xsrfToken) {
console.error('XSRF token not found in cookies');
return null;
}
const options = {
method: method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-XSRF-TOKEN': xsrfToken
},
credentials: 'include'
};
if (body) {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(`https://panel.sillydev.co.uk${endpoint}`, options);
// Handle non-OK responses
if (!response.ok) {
const errorData = await response.json();
throw new Error(JSON.stringify(errorData));
}
// For successful responses, check if there's actual content
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
// If no JSON content but response is OK, return true to indicate success
return true;
} catch (error) {
console.error(`API request failed:`, error);
return null;
}
}
// Get user's current balance
async function getUserBalance() {
const userData = await makeRequest('/api/client/store');
return userData?.attributes?.balance || 0;
}
// Get list of all servers
async function getServers() {
const serversData = await makeRequest('/api/client?page=1');
return serversData?.data || [];
}
// Renew a specific server
async function renewServer(serverUuid) {
return await makeRequest(`/api/client/servers/${serverUuid}/renew`, 'POST');
}
// Calculate optimal renewals based on current balance and server states
function calculateOptimalRenewals(servers, balance) {
// Calculate how many renewals we can afford
const maxRenewals = Math.floor(balance / RENEWAL_COST);
if (maxRenewals === 0) return [];
// Sort servers by a combination of days remaining and potential waste
const sortedServers = servers.map(server => {
const daysRemaining = server.attributes.renewal;
// Calculate potential waste (days that would exceed MAX_RENEWAL_DAYS)
const potentialWaste = Math.max(0, (daysRemaining + DAYS_PER_RENEWAL) - MAX_RENEWAL_DAYS);
// Calculate urgency score (lower is more urgent)
const urgencyScore = (daysRemaining * 2) + (potentialWaste * 3);
return {
server: server,
daysRemaining: daysRemaining,
potentialWaste: potentialWaste,
urgencyScore: urgencyScore
};
}).sort((a, b) => a.urgencyScore - b.urgencyScore);
const renewalPlan = [];
let remainingBalance = balance;
// Consider each server for renewal
for (const serverInfo of sortedServers) {
// Stop if we can't afford more renewals
if (remainingBalance < RENEWAL_COST) break;
const daysRemaining = serverInfo.daysRemaining;
const newTotalDays = daysRemaining + DAYS_PER_RENEWAL;
// Only renew if:
// 1. The new total won't exceed MAX_RENEWAL_DAYS
// 2. Current days are less than 14 (to ensure we always have a buffer for renewal)
if (newTotalDays <= MAX_RENEWAL_DAYS && daysRemaining < 14) {
renewalPlan.push({
uuid: serverInfo.server.attributes.uuid,
currentDays: daysRemaining,
newTotal: newTotalDays,
needsRenewal: true
});
remainingBalance -= RENEWAL_COST;
}
}
return renewalPlan;
}
// Main function to check and renew servers
async function checkAndRenewServers() {
console.log('Checking server renewals...');
// Get current balance and servers
const balance = await getUserBalance();
const servers = await getServers();
if (!balance || !servers) {
console.log('Failed to fetch necessary data');
return;
}
console.log(`Current balance: ${balance}`);
console.log('Current servers:', servers.map(s => ({
name: s.attributes.name,
days: s.attributes.renewal
})));
// Calculate optimal renewals
const renewalPlan = calculateOptimalRenewals(servers, balance);
// Execute renewals
for (const plan of renewalPlan) {
console.log(`Renewing server ${plan.uuid} (current days: ${plan.currentDays}, will have: ${plan.newTotal})`);
try {
const result = await renewServer(plan.uuid);
if (result === true) {
console.log(`Successfully renewed server ${plan.uuid}`);
} else {
console.error(`Failed to renew server ${plan.uuid}`);
}
} catch (error) {
console.error(`Failed to renew server ${plan.uuid}:`, error);
}
// Add a small delay between renewals to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 2000));
}
console.log('Renewal check completed');
}
// Function to wait for XSRF token to be available in cookies
function waitForXsrfToken() {
return new Promise((resolve) => {
const checkToken = () => {
const token = getXsrfToken();
if (token) {
resolve();
} else {
setTimeout(checkToken, 100);
}
};
checkToken();
});
}
// Start the periodic checks
async function initialize() {
console.log('SillyDev Auto Renewal script started, waiting for XSRF token...');
await waitForXsrfToken();
console.log('XSRF token found, starting renewal checks...');
checkAndRenewServers(); // Initial check
setInterval(checkAndRenewServers, CHECK_INTERVAL); // Periodic checks
}
initialize();
})(); Serv00服务器后台通过devil命令管理,常用功能收集:
开启用户程序或脚本执行权限
devil binexec on端口管理
添加端口:
devil port add [tcp,udp] 54321 # 指定端口
devil port add [tcp,udp] random # 随机分配端口查看端口:
devil port list删除端口:
devil port del [tcp,udp] 33333网站管理
添加网站:
devil www add xxx.your.domain proxy localhost 54321 # 代理到本地服务
devil www add xxx.your.domain [php] # php
devil www add xxx.your.domain pointer www.baidu.com
devil www add xxx.your.domain [python|nodejs|ruby] /home/xxxx/bin/xxx [production, staging, development, test]查看网站:
devil www list删除站点:
devil www del xxx.your.domain查看当前所在服务器IP
devil vhost list