diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-02-19 02:01:34 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-02-19 02:20:14 +0800 |
commit | 89e31c19bb8fca91c54a73ff7a7f4e837d1dbf93 (patch) | |
tree | e86894b16145f4b2f127c0a30a7383aaed17a325 | |
parent | 29bf91b8f57ec28492bb882d9f4d38fb12c9519a (diff) | |
download | crupest-89e31c19bb8fca91c54a73ff7a7f4e837d1dbf93.tar.gz crupest-89e31c19bb8fca91c54a73ff7a7f4e837d1dbf93.tar.bz2 crupest-89e31c19bb8fca91c54a73ff7a7f4e837d1dbf93.zip |
feat(auto-backup): use coscli.
-rw-r--r-- | crupest-words.txt | 8 | ||||
-rw-r--r-- | docker/auto-backup/.dockerignore | 2 | ||||
-rw-r--r-- | docker/auto-backup/AutoBackup/.dockerignore | 2 | ||||
-rw-r--r-- | docker/auto-backup/AutoBackup/.gitignore | 2 | ||||
-rw-r--r-- | docker/auto-backup/AutoBackup/AutoBackup.csproj | 10 | ||||
-rw-r--r-- | docker/auto-backup/AutoBackup/Program.cs | 121 | ||||
-rw-r--r-- | docker/auto-backup/AutoBackup/TencentCloudCOS.cs | 211 | ||||
-rw-r--r-- | docker/auto-backup/Dockerfile | 33 | ||||
-rwxr-xr-x | docker/auto-backup/daemon.bash | 97 | ||||
-rw-r--r-- | templates/docker-compose.yaml.template | 12 | ||||
-rw-r--r-- | tools/cru-py/cru/service/_config.py | 5 |
11 files changed, 95 insertions, 408 deletions
diff --git a/crupest-words.txt b/crupest-words.txt index d5e637c..47ed6f8 100644 --- a/crupest-words.txt +++ b/crupest-words.txt @@ -7,18 +7,23 @@ yuqian forgejo rspamd certbot +roundcube +roundcubemail # general cheatsheet aarch64 pythonpath gerrit +esmtp +tini # unix cpio kmod nproc sourceware +zstd # hurd gnumach @@ -42,6 +47,9 @@ sbuild sbuildrc schroot +# commercial +myqcloud + # misc geodata geoip diff --git a/docker/auto-backup/.dockerignore b/docker/auto-backup/.dockerignore deleted file mode 100644 index 7a09751..0000000 --- a/docker/auto-backup/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -AutoBackup/bin -AutoBackup/obj diff --git a/docker/auto-backup/AutoBackup/.dockerignore b/docker/auto-backup/AutoBackup/.dockerignore deleted file mode 100644 index 7de5508..0000000 --- a/docker/auto-backup/AutoBackup/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -obj -bin diff --git a/docker/auto-backup/AutoBackup/.gitignore b/docker/auto-backup/AutoBackup/.gitignore deleted file mode 100644 index 7de5508..0000000 --- a/docker/auto-backup/AutoBackup/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -obj -bin diff --git a/docker/auto-backup/AutoBackup/AutoBackup.csproj b/docker/auto-backup/AutoBackup/AutoBackup.csproj deleted file mode 100644 index 694035b..0000000 --- a/docker/auto-backup/AutoBackup/AutoBackup.csproj +++ /dev/null @@ -1,10 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <OutputType>Exe</OutputType> - <TargetFramework>net9.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - </PropertyGroup> - -</Project> diff --git a/docker/auto-backup/AutoBackup/Program.cs b/docker/auto-backup/AutoBackup/Program.cs deleted file mode 100644 index c2e7a0d..0000000 --- a/docker/auto-backup/AutoBackup/Program.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Read args to determine what file to upload
-
-const string DefaultUploadFilePath = "/tmp/data.tar.xz";
-string uploadFilePath = DefaultUploadFilePath;
-string? uploadDestinationPath = null;
-if (args.Length == 0)
-{
- Console.WriteLine("You don't specify the file to upload, will upload /tmp/data.tar.xz by default.");
- Console.WriteLine("You don't specify the destination to upload, will use timestamp with proper file extension.");
-}
-else if (args.Length == 1)
-{
- if (args[0].Length == 0)
- {
- Console.Error.WriteLine("File to upload can't be empty string.");
- Environment.Exit(2);
- }
- uploadFilePath = args[0];
- Console.WriteLine("You don't specify the destination to upload, will use timestamp with proper file extension.");
-}
-else if (args.Length == 2)
-{
- if (args[0].Length == 0)
- {
- Console.Error.WriteLine("File to upload can't be empty string.");
- Environment.Exit(2);
- }
-
- if (args[1].Length == 0)
- {
- Console.Error.WriteLine("Destination to upload can't be empty string.");
- Environment.Exit(2);
- }
-
- uploadFilePath = args[0];
- uploadDestinationPath = args[1];
-}
-else
-{
- // Write to stderr
- Console.Error.WriteLine("You can only specify one optional file and one optional destination to upload.");
- Environment.Exit(2);
-}
-
-// Check the upload exists
-if (!File.Exists(uploadFilePath))
-{
- Console.Error.WriteLine($"The file {uploadFilePath} doesn't exist.");
- Environment.Exit(3);
-}
-
-// Check the upload file is not a directory
-if (File.GetAttributes(uploadFilePath).HasFlag(FileAttributes.Directory))
-{
- Console.Error.WriteLine($"The file {uploadFilePath} is a directory.");
- Environment.Exit(4);
-}
-
-// Check the upload file is not bigger than 5G
-if (new FileInfo(uploadFilePath).Length > 5L * 1024L * 1024L * 1024L)
-{
- Console.Error.WriteLine($"The file {uploadFilePath} is bigger than 5G, which is not support now.");
- Environment.Exit(5);
-}
-
-// Get config from environment variables
-var configNameList = new List<string>{
- "CRUPEST_AUTO_BACKUP_COS_SECRET_ID",
- "CRUPEST_AUTO_BACKUP_COS_SECRET_KEY",
- "CRUPEST_AUTO_BACKUP_COS_REGION",
- "CRUPEST_AUTO_BACKUP_BUCKET_NAME"
-};
-
-var config = new Dictionary<string, string>();
-foreach (var configName in configNameList)
-{
- var configValue = Environment.GetEnvironmentVariable(configName);
- if (configValue is null)
- {
- Console.Error.WriteLine($"Environment variable {configName} is required.");
- Environment.Exit(5);
- }
- config.Add(configName, configValue);
-}
-
-var region = config["CRUPEST_AUTO_BACKUP_COS_REGION"];
-var secretId = config["CRUPEST_AUTO_BACKUP_COS_SECRET_ID"];
-var secretKey = config["CRUPEST_AUTO_BACKUP_COS_SECRET_KEY"];
-var bucketName = config["CRUPEST_AUTO_BACKUP_BUCKET_NAME"];
-
-var credentials = new TencentCloudCOSHelper.Credentials(secretId, secretKey);
-
-if (uploadDestinationPath is null)
-{
- var uploadFileName = Path.GetFileName(uploadFilePath);
- var firstDotPosition = uploadFileName.IndexOf('.');
- uploadDestinationPath = DateTime.Now.ToString("s");
- if (firstDotPosition != -1)
- {
- uploadDestinationPath += uploadFileName.Substring(firstDotPosition + 1);
- }
-}
-
-Console.WriteLine($"Upload file source: {uploadFilePath}");
-Console.WriteLine($"Upload COS region: {config["CRUPEST_AUTO_BACKUP_COS_REGION"]}");
-Console.WriteLine($"Upload bucket name: {config["CRUPEST_AUTO_BACKUP_BUCKET_NAME"]}");
-Console.WriteLine($"Upload file destination: {uploadDestinationPath}");
-
-await using var fileStream = new FileStream(uploadFilePath, FileMode.Open, FileAccess.Read);
-
-// 上传对象
-try
-{
- await TencentCloudCOSHelper.PutObject(credentials, region, bucketName, uploadDestinationPath, fileStream);
- Console.WriteLine("Upload completed!");
-}
-catch (Exception e)
-{
- Console.Error.WriteLine("Exception: " + e);
- Environment.Exit(6);
-}
diff --git a/docker/auto-backup/AutoBackup/TencentCloudCOS.cs b/docker/auto-backup/AutoBackup/TencentCloudCOS.cs deleted file mode 100644 index 28d032c..0000000 --- a/docker/auto-backup/AutoBackup/TencentCloudCOS.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System.Net; -using System.Security.Cryptography; -using System.Text; - - -public static class TencentCloudCOSHelper -{ - public class Credentials - { - public Credentials(string secretId, string secretKey) - { - SecretId = secretId; - SecretKey = secretKey; - } - - public string SecretId { get; } - public string SecretKey { get; } - } - - public class RequestInfo - { - public RequestInfo(string method, string urlPathname, IEnumerable<KeyValuePair<string, string>> parameters, IEnumerable<KeyValuePair<string, string>> headers) - { - Method = method; - UrlPathname = urlPathname; - Parameters = new Dictionary<string, string>(parameters); - Headers = new Dictionary<string, string>(headers); - } - - public string Method { get; } - public string UrlPathname { get; } - public IReadOnlyDictionary<string, string> Parameters { get; } - public IReadOnlyDictionary<string, string> Headers { get; } - } - - public class TimeDuration - { - public TimeDuration(DateTimeOffset start, DateTimeOffset end) - { - if (start > end) - { - throw new ArgumentException("Start time must be earlier than end time."); - } - - Start = start; - End = end; - } - - public DateTimeOffset Start { get; } - public DateTimeOffset End { get; } - } - - public static string GenerateSign(Credentials credentials, RequestInfo request, TimeDuration signValidTime) - { - List<(string key, string value)> Transform(IEnumerable<KeyValuePair<string, string>> raw) - { - if (raw == null) - return new List<(string key, string value)>(); - - var sorted = raw.Select(p => (key: WebUtility.UrlEncode(p.Key.ToLower()), value: WebUtility.UrlEncode(p.Value))).ToList(); - sorted.Sort((left, right) => string.CompareOrdinal(left.key, right.key)); - return sorted; - } - - var transformedParameters = Transform(request.Parameters); - var transformedHeaders = Transform(request.Headers); - - - const string signAlgorithm = "sha1"; - - static string ByteArrayToString(byte[] bytes) - { - return BitConverter.ToString(bytes).Replace("-", "").ToLower(); - } - - var keyTime = $"{signValidTime.Start.ToUnixTimeSeconds().ToString()};{signValidTime.End.ToUnixTimeSeconds().ToString()}"; - using HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes(credentials.SecretKey)); - var signKey = ByteArrayToString(hmac.ComputeHash(Encoding.UTF8.GetBytes(keyTime))); - - static string Join(IEnumerable<(string key, string value)> raw) - { - return string.Join('&', raw.Select(p => string.Concat(p.key, "=", p.value))); - } - - var httpParameters = Join(transformedParameters); - var urlParamList = string.Join(';', transformedParameters.Select(p => p.key)); - var httpHeaders = Join(transformedHeaders); - var headerList = string.Join(';', transformedHeaders.Select(h => h.key)); - - var httpString = new StringBuilder() - .Append(request.Method.ToLower()).Append('\n') - .Append(request.UrlPathname).Append('\n') - .Append(httpParameters).Append('\n') - .Append(httpHeaders).Append('\n') - .ToString(); - - using var sha1 = SHA1.Create(); - string Sha1(string data) - { - var result = sha1.ComputeHash(Encoding.UTF8.GetBytes(data)); - return ByteArrayToString(result); - } - - var stringToSign = new StringBuilder() - .Append(signAlgorithm).Append('\n') - .Append(keyTime).Append('\n') - .Append(Sha1(httpString)).Append('\n') - .ToString(); - - hmac.Key = Encoding.UTF8.GetBytes(signKey); - var signature = ByteArrayToString(hmac.ComputeHash( - Encoding.UTF8.GetBytes(stringToSign))); - - - List<(string, string)> result = new List<(string, string)>(); - result.Add(("q-sign-algorithm", signAlgorithm)); - result.Add(("q-ak", credentials.SecretId)); - result.Add(("q-sign-time", keyTime)); - result.Add(("q-key-time", keyTime)); - result.Add(("q-header-list", headerList)); - result.Add(("q-url-param-list", urlParamList)); - result.Add(("q-signature", signature)); - return Join(result); - } - - private static string GetHost(string bucket, string region) - { - return $"{bucket}.cos.{region}.myqcloud.com"; - } - - public static async Task<bool> IsObjectExists(Credentials credentials, string region, string bucket, string key) - { - var host = GetHost(bucket, region); - var encodedKey = WebUtility.UrlEncode(key); - - using var request = new HttpRequestMessage(); - request.Method = HttpMethod.Head; - request.RequestUri = new Uri($"https://{host}/{encodedKey}"); - request.Headers.Host = host; - request.Headers.Date = DateTimeOffset.Now; - request.Headers.TryAddWithoutValidation("Authorization", GenerateSign(credentials, new RequestInfo( - "head", "/" + key, new Dictionary<string, string>(), - new Dictionary<string, string> - { - ["Host"] = host - } - ), new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(5)))); - - using var client = new HttpClient(); - using var response = await client.SendAsync(request); - - if (response.IsSuccessStatusCode) - return true; - if (response.StatusCode == HttpStatusCode.NotFound) - return false; - - throw new Exception($"Unknown response code. {response.ToString()}"); - } - - public static async Task PutObject(Credentials credentials, string region, string bucket, string key, Stream dataStream) - { - if (!dataStream.CanSeek) - { - throw new ArgumentException("Data stream must be seekable."); - } - - if (dataStream.Seek(0, SeekOrigin.End) > 5L * 1024L * 1024L * 1024L) - { - throw new ArgumentException("Data stream must be smaller than 5GB."); - } - - dataStream.Seek(0, SeekOrigin.Begin); - - var host = GetHost(bucket, region); - var encodedKey = WebUtility.UrlEncode(key); - using var md5Handler = MD5.Create(); - var md5 = Convert.ToBase64String(await md5Handler.ComputeHashAsync(dataStream)); - - dataStream.Seek(0, SeekOrigin.Begin); - - const string kContentMD5HeaderName = "Content-MD5"; - - using var httpRequest = new HttpRequestMessage() - { - Method = HttpMethod.Put, - RequestUri = new Uri($"https://{host}/{encodedKey}") - }; - httpRequest.Headers.Host = host; - httpRequest.Headers.Date = DateTimeOffset.Now; - - using var httpContent = new StreamContent(dataStream); - httpContent.Headers.Add(kContentMD5HeaderName, md5); - httpRequest.Content = httpContent; - - var signedHeaders = new Dictionary<string, string> - { - ["Host"] = host, - [kContentMD5HeaderName] = md5 - }; - - httpRequest.Headers.TryAddWithoutValidation("Authorization", GenerateSign(credentials, new RequestInfo( - "put", "/" + key, new Dictionary<string, string>(), signedHeaders - ), new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(10)))); - - using var client = new HttpClient(); - using var response = await client.SendAsync(httpRequest); - - if (!response.IsSuccessStatusCode) - throw new Exception($"Not success status code: {response.StatusCode}\n{await response.Content.ReadAsStringAsync()}"); - } -}
\ No newline at end of file diff --git a/docker/auto-backup/Dockerfile b/docker/auto-backup/Dockerfile index c7ff4fc..943c96f 100644 --- a/docker/auto-backup/Dockerfile +++ b/docker/auto-backup/Dockerfile @@ -1,24 +1,15 @@ -FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build -COPY AutoBackup /AutoBackup -WORKDIR /AutoBackup -RUN dotnet publish AutoBackup.csproj --configuration Release --output ./publish/ -r linux-x64 --self-contained false +FROM debian +RUN apt-get update && apt-get install -y \ + tini ca-certificates coreutils bash tar zstd \ + && rm -rf /var/lib/apt/lists/* + +ENV CRUPEST_AUTO_BACKUP_INIT_DELAY= +ENV CRUPEST_AUTO_BACKUP_INTERVAL=1d + +ADD --chmod=755 https://github.com/tencentyun/coscli/releases/download/v1.0.3/coscli-v1.0.3-linux-amd64 /app/coscli +ADD --chmod=755 daemon.bash /app/ -FROM mcr.microsoft.com/dotnet/runtime:9.0-alpine -RUN apk add --no-cache tini coreutils bash tar xz -ARG CRUPEST_AUTO_BACKUP_INIT_DELAY=0 -ARG CRUPEST_AUTO_BACKUP_INTERVAL=1d -ARG CRUPEST_AUTO_BACKUP_COS_SECRET_ID -ARG CRUPEST_AUTO_BACKUP_COS_SECRET_KEY -ARG CRUPEST_AUTO_BACKUP_COS_REGION -ARG CRUPEST_AUTO_BACKUP_BUCKET_NAME -ENV CRUPEST_AUTO_BACKUP_INIT_DELAY=${CRUPEST_AUTO_BACKUP_INIT_DELAY} -ENV CRUPEST_AUTO_BACKUP_INTERVAL=${CRUPEST_AUTO_BACKUP_INTERVAL} -ENV CRUPEST_AUTO_BACKUP_COS_SECRET_ID=${CRUPEST_AUTO_BACKUP_COS_SECRET_ID} -ENV CRUPEST_AUTO_BACKUP_COS_SECRET_KEY=${CRUPEST_AUTO_BACKUP_COS_SECRET_KEY} -ENV CRUPEST_AUTO_BACKUP_COS_REGION=${CRUPEST_AUTO_BACKUP_COS_REGION} -ENV CRUPEST_AUTO_BACKUP_BUCKET_NAME=${CRUPEST_AUTO_BACKUP_BUCKET_NAME} VOLUME [ "/data" ] -COPY daemon.bash /daemon.bash -COPY --from=build /AutoBackup/publish /AutoBackup + ENTRYPOINT ["tini", "--"] -CMD [ "/daemon.bash" ] +CMD [ "/app/daemon.bash" ] diff --git a/docker/auto-backup/daemon.bash b/docker/auto-backup/daemon.bash index a4dd5dc..0c6beec 100755 --- a/docker/auto-backup/daemon.bash +++ b/docker/auto-backup/daemon.bash @@ -2,56 +2,93 @@ set -e +die() { + echo -e "\033[31mError: " "$@" "\033[0m" >&2 + exit 1 +} + +note() { + echo -e "\033[33mNote: " "$@" "\033[0m" +} + +success() { + echo -e "\033[32mSuccess: " "$@" "\033[0m" +} + # Check I'm root. if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root" 1>&2 - exit 1 + die "This script must be run as root" fi +if [[ ! -f /run/secrets/auto-backup ]]; then + die "/run/secrets/auto-backup not found, please use docker secrets to set it." +fi -# Check xz, tar and coscmd -xz --version +if [[ -z "$CRUPEST_AUTO_BACKUP_INTERVAL" ]]; then + die "Backup interval not set, please set it!" +fi + +# shellcheck source=/dev/null +. /run/secrets/auto-backup + +note "Checking secrets..." +[[ -n "$CRUPEST_AUTO_BACKUP_COS_ENDPOINT" ]] || die "COS endpoint not set!" +[[ -n "$CRUPEST_AUTO_BACKUP_COS_BUCKET" ]] || die "COS bucket not set!" +[[ -n "$CRUPEST_AUTO_BACKUP_COS_SECRET_ID" ]] || die "COS secret ID not set!" +[[ -n "$CRUPEST_AUTO_BACKUP_COS_SECRET_KEY" ]] || die "COS secret key not set!" +success "Secrets check passed." + +note "Checking tools..." tar --version +zstd --version +/app/coscli --version +success "Tools check passed." + +echo "Backup interval set to $CRUPEST_AUTO_BACKUP_INTERVAL..." + +if [[ -z "$CRUPEST_AUTO_BACKUP_INIT_DELAY" ]]; then + echo "Initial delay not set, will do a backup immediately!" +else + echo "Initial delay set to $CRUPEST_AUTO_BACKUP_INIT_DELAY ..." + sleep "$CRUPEST_AUTO_BACKUP_INIT_DELAY" +fi function backup { - # Output "Begin backup..." in yellow and restore default - echo -e "\e[0;103m\e[K\e[1mBegin backup..." "\e[0m" + note "Begin backup..." - # Get current time and convert it to YYYY-MM-DDTHH:MM:SSZ - current_time=$(date +%Y-%m-%dT%H:%M:%SZ) - echo "Current time: $current_time" + # Get current time and convert it to YYYY-MM-DDTHH:MM:SSZ + current_time="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + echo "Current time UTC: $current_time" - echo "Create tar.xz for data..." + backup_file_ext="tar.zst" + tmp_file="/tmp/data.$backup_file_ext" - # tar and xz /data to tmp - tar -cJf /tmp/data.tar.xz -C / data + echo "Create $tmp_file for data..." + tar -cp --zstd -f "$tmp_file" -C / data - # Output /tmp/data.tar.xz size - du -h /tmp/data.tar.xz | cut -f1 | xargs echo "Size of data.tar.xz:" + du -h "$tmp_file" | cut -f1 | xargs echo "Size of $tmp_file:" - destination="${current_time}.tar.xz" + des_file_name="$current_time.$backup_file_ext" + echo "Upload $des_file_name to COS..." - # upload to remote - dotnet /AutoBackup/AutoBackup.dll /tmp/data.tar.xz "$destination" + /app/coscli --init-skip \ + --secret-id "${CRUPEST_AUTO_BACKUP_COS_SECRET_ID}" \ + --secret-key "${CRUPEST_AUTO_BACKUP_COS_SECRET_KEY}" \ + --endpoint "${CRUPEST_AUTO_BACKUP_COS_ENDPOINT}" \ + cp "$tmp_file" "cos://${CRUPEST_AUTO_BACKUP_COS_BUCKET}/$des_file_name" - echo "Remove tmp file..." - # remove tmp - rm /tmp/data.tar.xz + echo "Remove tmp file..." + rm "$tmp_file" - echo "$destination" >> /data/backup.log + echo "$des_file_name" >>/data/backup.log - # echo "Backup finished!" in green and restore default - echo -e "\e[0;102m\e[K\e[1mFinish backup!\e[0m" + success "Finish backup!" } -echo "Initial delay: $CRUPEST_AUTO_BACKUP_INIT_DELAY" -sleep "$CRUPEST_AUTO_BACKUP_INIT_DELAY" - # forever loop while true; do - backup + backup - # sleep for CRUPEST_AUTO_BACKUP_INTERVAL - echo "Sleep for $CRUPEST_AUTO_BACKUP_INTERVAL for next backup..." - sleep "$CRUPEST_AUTO_BACKUP_INTERVAL" + echo "Sleep for $CRUPEST_AUTO_BACKUP_INTERVAL for next backup..." + sleep "$CRUPEST_AUTO_BACKUP_INTERVAL" done diff --git a/templates/docker-compose.yaml.template b/templates/docker-compose.yaml.template index 124c9d5..d55c7c2 100644 --- a/templates/docker-compose.yaml.template +++ b/templates/docker-compose.yaml.template @@ -78,16 +78,11 @@ services: context: ./docker/auto-backup dockerfile: Dockerfile pull: true - args: - - CRUPEST_AUTO_BACKUP_COS_SECRET_ID=${CRUPEST_AUTO_BACKUP_COS_SECRET_ID} - - CRUPEST_AUTO_BACKUP_COS_SECRET_KEY=${CRUPEST_AUTO_BACKUP_COS_SECRET_KEY} - - CRUPEST_AUTO_BACKUP_COS_REGION=${CRUPEST_AUTO_BACKUP_COS_REGION} - - CRUPEST_AUTO_BACKUP_BUCKET_NAME=${CRUPEST_AUTO_BACKUP_BUCKET_NAME} - tags: - - "crupest/auto-backup:latest" container_name: auto-backup volumes: - "./data:/data" + secrets: + - auto-backup restart: on-failure:3 mailserver: @@ -203,5 +198,8 @@ volumes: roundcubemail-temp: secrets: + auto-backup: + file: data/config + git-server: file: data/config diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py index e4ab966..1394ee4 100644 --- a/tools/cru-py/cru/service/_config.py +++ b/tools/cru-py/cru/service/_config.py @@ -190,10 +190,11 @@ class ConfigManager(AppCommandFeatureProvider): "access key secret for Tencent COS, used for auto backup", ) _add_text( - "AUTO_BACKUP_COS_REGION", "region for Tencent COS, used for auto backup" + "AUTO_BACKUP_COS_ENDPOINT", + "endpoint (cos.*.myqcloud.com) for Tencent COS, used for auto backup", ) _add_text( - "AUTO_BACKUP_BUCKET_NAME", + "AUTO_BACKUP_COS_BUCKET", "bucket name for Tencent COS, used for auto backup", ) _add_text("GITHUB_USERNAME", "github username for fetching todos") |